Skip to content

Commit ec1b4ce

Browse files
authored
Merge pull request #348 from DataDog/jb/marking_fix
Add alignment checks to NativeFunc mark methods
2 parents 3b0a29f + 9349714 commit ec1b4ce

10 files changed

Lines changed: 455 additions & 42 deletions

File tree

.github/actions/upsert-pr-comment/action.yml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ inputs:
77
body-file:
88
description: "Path to file whose contents become the comment body"
99
required: true
10+
comment-id:
11+
description: "Unique identifier for this comment type (used to find/update existing comment)"
12+
required: false
13+
default: "upsert-pr-comment"
1014
repo: # optional; defaults to triggering repo
1115
description: "Repository (owner/repo)."
1216
required: false
@@ -31,25 +35,30 @@ runs:
3135
env:
3236
GH_TOKEN: ${{ steps.octo-sts.outputs.token }}
3337
BODY_FILE: ${{ inputs['body-file'] }}
38+
COMMENT_ID: ${{ inputs['comment-id'] }}
3439
REPO: ${{ inputs.repo || github.repository }}
3540
PR: ${{ inputs['pr-number'] || github.event.pull_request.number }}
3641
shell: bash
3742
run: |
3843
if [[ -s "$BODY_FILE" ]]; then
3944
set -e
40-
# find last comment by this actor
41-
# first, build a jq filter that embeds the actor’s login
42-
filter=".[] | select(.user.login == \"${GITHUB_ACTOR}\") | .id"
45+
# Create marker to identify this comment type
46+
MARKER="<!-- ${COMMENT_ID} -->"
47+
48+
# Find existing comment with this marker
4349
cid=$(gh api "repos/$REPO/issues/$PR/comments?per_page=100" \
44-
--jq "${filter}" | tail -n1)
45-
50+
--jq ".[] | select(.body | contains(\"${MARKER}\")) | .id" | head -n1)
51+
52+
# Prepend marker to body
53+
BODY="${MARKER}"$'\n'"$(< "$BODY_FILE")"
54+
4655
if [[ -n "$cid" ]]; then
4756
gh api --method PATCH "repos/$REPO/issues/comments/$cid" \
48-
--raw-field body="$(< "$BODY_FILE")"
57+
--raw-field body="$BODY"
4958
echo "✏️ Updated comment $cid"
5059
else
5160
gh api --method POST "repos/$REPO/issues/$PR/comments" \
52-
--raw-field body="$(< "$BODY_FILE")"
61+
--raw-field body="$BODY"
5362
echo "💬 Created new comment"
5463
fi
5564
else

.github/workflows/ci.yml

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ on:
1111
tags-ignore:
1212
- v*
1313
pull_request:
14+
types: [opened, synchronize, reopened, labeled]
1415
workflow_dispatch:
1516

1617
permissions:
@@ -24,20 +25,18 @@ jobs:
2425
outputs:
2526
skip: ${{ steps.check.outputs.skip }}
2627
steps:
27-
- name: Check if PR exists
28+
- name: Check if PR exists for this branch (skip push if PR exists)
2829
id: check
2930
env:
3031
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31-
HEAD_REF: ${{ github.head_ref }}
32+
HEAD_REF: ${{ github.ref_name }}
3233
run: |
34+
# On push events, base_ref is empty; skip if a PR already exists for this branch
3335
if [ -z "${{ github.base_ref }}" ]; then
3436
prs=$(gh pr list \
3537
--repo "$GITHUB_REPOSITORY" \
36-
--json baseRefName,headRefName \
37-
--jq '
38-
map(select(.baseRefName == "${{ github.base_ref }}" and .headRefName == "$HEAD_REF}"))
39-
| length
40-
')
38+
--json headRefName \
39+
--jq "[.[] | select(.headRefName == env.HEAD_REF)] | length")
4140
if ((prs > 0)); then
4241
echo "skip=true" >> "$GITHUB_OUTPUT"
4342
fi
@@ -116,19 +115,49 @@ jobs:
116115
# This ensures javadoc validation matches the exact conditions used during publishing
117116
./gradlew :ddprof-lib:javadoc --no-daemon --parallel --build-cache --no-watch-fs
118117
118+
compute-configurations:
119+
runs-on: ubuntu-latest
120+
needs: check-for-pr
121+
if: needs.check-for-pr.outputs.skip != 'true'
122+
outputs:
123+
configurations: ${{ steps.compute.outputs.configurations }}
124+
steps:
125+
- name: Debounce label events
126+
if: github.event.action == 'labeled'
127+
run: |
128+
echo "Waiting 30s to allow adding multiple labels..."
129+
sleep 30
130+
- name: Compute test configurations from PR labels
131+
id: compute
132+
env:
133+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
134+
run: |
135+
# Always include debug
136+
configs='["debug"'
137+
138+
# For PRs, check labels; for push/workflow_dispatch, use debug only
139+
if [ "${{ github.event_name }}" = "pull_request" ]; then
140+
labels=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} --jq '.labels[].name' 2>/dev/null) || labels=""
141+
142+
if echo "$labels" | grep -Fq "test:release"; then
143+
configs="$configs"',"release"'
144+
fi
145+
if echo "$labels" | grep -Fq "test:asan"; then
146+
configs="$configs"',"asan"'
147+
fi
148+
if echo "$labels" | grep -Fq "test:tsan"; then
149+
configs="$configs"',"tsan"'
150+
fi
151+
fi
152+
153+
configs="$configs]"
154+
echo "configurations=$configs" >> $GITHUB_OUTPUT
155+
echo "Test configurations: $configs"
156+
119157
test-matrix:
120-
needs: check-formatting
158+
needs: [check-for-pr, check-formatting, compute-configurations]
121159
if: needs.check-for-pr.outputs.skip != 'true'
122160
uses: ./.github/workflows/test_workflow.yml
123161
with:
124-
configuration: '["debug"]'
162+
configuration: ${{ needs.compute-configurations.outputs.configurations }}
125163

126-
gh-release:
127-
if: startsWith(github.event.ref, 'refs/heads/release/')
128-
runs-on: ubuntu-latest
129-
needs: [test-matrix]
130-
steps:
131-
- name: Create Github Release
132-
uses: ./.github/workflows/gh_release.yml@gh-release
133-
with:
134-
release_branch: ${GITHUB_REF_NAME}

.github/workflows/codecheck.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ jobs:
5252
uses: ./.github/actions/upsert-pr-comment
5353
with:
5454
body-file: comment.html
55+
comment-id: scan-build-report
5556

5657
codeql:
5758
if: needs.check-for-pr.outputs.skip != 'true'

ddprof-lib/src/main/cpp/codeCache.h

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,40 @@ class NativeFunc {
6464
static void destroy(char *name);
6565

6666
static short libIndex(const char *name) {
67+
if (name == nullptr) {
68+
return -1;
69+
}
6770
NativeFunc* func = from(name);
6871
if (!is_aligned(func, sizeof(func))) {
6972
return -1;
7073
}
7174
return func->_lib_index;
7275
}
7376

74-
static bool isMarked(const char *name) { return from(name)->_mark != 0; }
77+
static bool is_marked(const char *name) {
78+
return read_mark(name) != 0;
79+
}
7580

76-
static char mark(const char* name) {
77-
return from(name)->_mark;
81+
static char read_mark(const char* name) {
82+
if (name == nullptr) {
83+
return 0;
84+
}
85+
NativeFunc* func = from(name);
86+
if (!is_aligned(func, sizeof(func))) {
87+
return 0;
88+
}
89+
return func->_mark;
7890
}
7991

80-
static void mark(const char* name, char value) {
81-
from(name)->_mark = value;
92+
static void set_mark(const char* name, char value) {
93+
if (name == nullptr) {
94+
return;
95+
}
96+
NativeFunc* func = from(name);
97+
if (!is_aligned(func, sizeof(func))) {
98+
return;
99+
}
100+
func->_mark = value;
82101
}
83102
};
84103

@@ -205,13 +224,13 @@ class CodeCache {
205224
for (int i = 0; i < _count; i++) {
206225
const char* blob_name = _blobs[i]._name;
207226
if (blob_name != NULL && predicate(blob_name)) {
208-
NativeFunc::mark(blob_name, value);
227+
NativeFunc::set_mark(blob_name, value);
209228
}
210229
}
211230

212231
if (value == MARK_VM_RUNTIME && _name != NULL) {
213232
// In case a library has no debug symbols
214-
NativeFunc::mark(_name, value);
233+
NativeFunc::set_mark(_name, value);
215234
}
216235
}
217236

ddprof-lib/src/main/cpp/profiler.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ Profiler::NativeFrameResolution Profiler::resolveNativeFrameForWalkVM(uintptr_t
375375
// Get symbol name and check mark
376376
const char *method_name = nullptr;
377377
lib->binarySearch((void*)pc, &method_name);
378-
char mark = (method_name != nullptr) ? NativeFunc::mark(method_name) : 0;
378+
char mark = (method_name != nullptr) ? NativeFunc::read_mark(method_name) : 0;
379379

380380
if (mark != 0) {
381381
return {nullptr, BCI_NATIVE_FRAME, true}; // Marked - stop processing
@@ -395,7 +395,7 @@ Profiler::NativeFrameResolution Profiler::resolveNativeFrameForWalkVM(uintptr_t
395395

396396
// Fallback: Traditional symbol resolution
397397
const char *method_name = findNativeMethod((void*)pc);
398-
if (method_name != nullptr && NativeFunc::isMarked(method_name)) {
398+
if (method_name != nullptr && NativeFunc::is_marked(method_name)) {
399399
return {nullptr, BCI_NATIVE_FRAME, true};
400400
}
401401

@@ -427,7 +427,7 @@ int Profiler::convertNativeTrace(int native_frames, const void **callchain,
427427
// binarySearch() returns symbol name, then we check mark (O(1))
428428
const char *method_name = nullptr;
429429
lib->binarySearch((void*)pc, &method_name);
430-
char mark = (method_name != nullptr) ? NativeFunc::mark(method_name) : 0;
430+
char mark = (method_name != nullptr) ? NativeFunc::read_mark(method_name) : 0;
431431

432432
if (mark != 0) {
433433
// Terminate scan at marked frame
@@ -450,7 +450,7 @@ int Profiler::convertNativeTrace(int native_frames, const void **callchain,
450450

451451
// Fallback: Traditional symbol resolution
452452
const char *method_name = findNativeMethod((void*)pc);
453-
if (method_name != nullptr && NativeFunc::isMarked(method_name)) {
453+
if (method_name != nullptr && NativeFunc::is_marked(method_name)) {
454454
// Terminate scan at marked frame
455455
return depth;
456456
}

ddprof-lib/src/main/cpp/stackWalker.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ __attribute__((no_sanitize("address"))) int StackWalker::walkVM(void* ucontext,
471471
const char* method_name = (const char*)resolution.method_id;
472472
int frame_bci = resolution.bci;
473473
char mark;
474-
if (frame_bci != BCI_NATIVE_FRAME_REMOTE && method_name != NULL && (mark = NativeFunc::mark(method_name)) != 0) {
474+
if (frame_bci != BCI_NATIVE_FRAME_REMOTE && method_name != NULL && (mark = NativeFunc::read_mark(method_name)) != 0) {
475475
if (mark == MARK_ASYNC_PROFILER && event_type == MALLOC_SAMPLE) {
476476
// Skip all internal frames above malloc_hook functions, leave the hook itself
477477
depth = 0;
@@ -516,7 +516,7 @@ __attribute__((no_sanitize("address"))) int StackWalker::walkVM(void* ucontext,
516516
Profiler::NativeFrameResolution prev_resolution = profiler->resolveNativeFrameForWalkVM((uintptr_t)prev_native_pc, lock_index);
517517
const char* prev_method_name = (const char*)prev_resolution.method_id;
518518
if (prev_method_name != NULL) {
519-
char prev_mark = NativeFunc::mark(prev_method_name);
519+
char prev_mark = NativeFunc::read_mark(prev_method_name);
520520
if (prev_mark == MARK_THREAD_ENTRY) {
521521
// Thread entry point detected in previous frame (Rust thread_start, libc start_thread, etc.)
522522
// This is the root frame - stop unwinding

ddprof-lib/src/main/cpp/utils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ inline bool is_aligned(const T* ptr, size_t alignment) noexcept {
1616
auto iptr = reinterpret_cast<uintptr_t>(ptr);
1717

1818
// Check if the integer value is a multiple of the alignment
19-
return ((iptr & (~(alignment - 1))) == 0);
19+
return ((iptr & (alignment - 1)) == 0);
2020
}
2121

2222
inline size_t align_down(size_t size, size_t alignment) noexcept {

0 commit comments

Comments
 (0)