Skip to content

Commit

Permalink
cstack=flat
Browse files Browse the repository at this point in the history
  • Loading branch information
apangin committed Dec 5, 2023
1 parent 29b78f4 commit 51aec72
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 32 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ $ asprof -d 30 -f /tmp/flamegraph.html 8983
* `--cstack MODE` - how to walk native frames (C stack). Possible modes are
`fp` (Frame Pointer), `dwarf` (DWARF unwind info),
`lbr` (Last Branch Record, available on Haswell since Linux 4.1),
`vm` (HotSpot VM Structs) and `no` (do not collect C stack).
`vm` (HotSpot VM Structs), `flat` (top frame only) and `no` (do not collect C stack).

By default, C stack is shown in cpu, itimer, wall-clock and perf-events profiles.
Java-level events like `alloc` and `lock` collect only Java stack.
Expand Down
6 changes: 3 additions & 3 deletions src/arguments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ static const Multiplier UNIVERSAL[] = {{'n', 1}, {'u', 1000}, {'m', 1000000}, {'
// threads - profile different threads separately
// sched - group threads by scheduling policy
// cstack=MODE - how to collect C stack frames in addition to Java stack
// MODE is 'fp', 'dwarf', 'lbr', 'vm' or 'no'
// MODE is 'fp', 'dwarf', 'lbr', 'vm', 'flat' or 'no'
// clock=SOURCE - clock source for JFR timestamps: 'tsc' or 'monotonic'
// allkernel - include only kernel-mode events
// alluser - include only user-mode events
Expand Down Expand Up @@ -343,7 +343,7 @@ Error Arguments::parse(const char* args) {
case 'd': _cstack = CSTACK_DWARF; break;
case 'l': _cstack = CSTACK_LBR; break;
case 'v': _cstack = CSTACK_VM; break;
default: _cstack = CSTACK_FP;
default: _cstack = (strcmp(value, "flat") == 0) ? CSTACK_FLAT : CSTACK_FP;
}
}

Expand Down Expand Up @@ -413,7 +413,7 @@ Error Arguments::parse(const char* args) {
if (_output == OUTPUT_SVG) {
return Error("SVG format is obsolete, use .html for FlameGraph");
}
_dump_traces = 100;
_dump_traces = _cstack == CSTACK_FLAT ? 0 : 100;
_dump_flat = 200;
}

Expand Down
3 changes: 2 additions & 1 deletion src/arguments.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ enum SHORT_ENUM CStack {
CSTACK_FP,
CSTACK_DWARF,
CSTACK_LBR,
CSTACK_VM
CSTACK_VM,
CSTACK_FLAT
};

enum SHORT_ENUM Clock {
Expand Down
2 changes: 1 addition & 1 deletion src/launcher/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ static const char USAGE_STRING[] =
" --total accumulate the total value (time, bytes, etc.)\n"
" --all-user only include user-mode events\n"
" --sched group threads by scheduling policy\n"
" --cstack mode how to traverse C stack: fp|dwarf|lbr|no\n"
" --cstack mode how to traverse C stack: fp|dwarf|lbr|vm|flat|no\n"
" --signal num use alternative signal for cpu or wall clock profiling\n"
" --clock source clock source for JFR timestamps: tsc|monotonic\n"
" --begin function begin profiling when function is executed\n"
Expand Down
54 changes: 29 additions & 25 deletions src/profiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -655,34 +655,38 @@ u64 Profiler::recordSample(void* ucontext, u64 counter, EventType event_type, Ev
num_frames = makeFrame(frames, frame_type, event->id());
}

StackContext java_ctx = {0};
num_frames += getNativeTrace(ucontext, frames + num_frames, event_type, tid, &java_ctx);

if (_cstack == CSTACK_VM) {
num_frames += StackWalker::walkVM(ucontext, frames + num_frames, _max_stack_depth);
} else if (event_type <= EXECUTION_SAMPLE) {
// Async events
int java_frames = getJavaTraceAsync(ucontext, frames + num_frames, _max_stack_depth, &java_ctx);
if (java_frames > 0 && java_ctx.pc != NULL && VMStructs::hasMethodStructs()) {
NMethod* nmethod = CodeHeap::findNMethod(java_ctx.pc);
if (nmethod != NULL) {
fillFrameTypes(frames + num_frames, java_frames, nmethod);
if (_cstack == CSTACK_FLAT) {
num_frames += StackWalker::flat(ucontext, frames + num_frames);
} else {
StackContext java_ctx = {0};
num_frames += getNativeTrace(ucontext, frames + num_frames, event_type, tid, &java_ctx);

if (_cstack == CSTACK_VM) {
num_frames += StackWalker::walkVM(ucontext, frames + num_frames, _max_stack_depth);
} else if (event_type <= EXECUTION_SAMPLE) {
// Async events
int java_frames = getJavaTraceAsync(ucontext, frames + num_frames, _max_stack_depth, &java_ctx);
if (java_frames > 0 && java_ctx.pc != NULL && VMStructs::hasMethodStructs()) {
NMethod* nmethod = CodeHeap::findNMethod(java_ctx.pc);
if (nmethod != NULL) {
fillFrameTypes(frames + num_frames, java_frames, nmethod);
}
}
num_frames += java_frames;
} else if (event_type >= ALLOC_SAMPLE && event_type <= ALLOC_OUTSIDE_TLAB && _alloc_engine == &alloc_tracer) {
if (VMStructs::_get_stack_trace != NULL) {
// Object allocation in HotSpot happens at known places where it is safe to call JVM TI,
// but not directly, since the thread is in_vm rather than in_native
num_frames += getJavaTraceInternal(jvmti_frames + num_frames, frames + num_frames, _max_stack_depth);
} else {
num_frames += getJavaTraceAsync(ucontext, frames + num_frames, _max_stack_depth, &java_ctx);
}
}
num_frames += java_frames;
} else if (event_type >= ALLOC_SAMPLE && event_type <= ALLOC_OUTSIDE_TLAB && _alloc_engine == &alloc_tracer) {
if (VMStructs::_get_stack_trace != NULL) {
// Object allocation in HotSpot happens at known places where it is safe to call JVM TI,
// but not directly, since the thread is in_vm rather than in_native
num_frames += getJavaTraceInternal(jvmti_frames + num_frames, frames + num_frames, _max_stack_depth);
} else {
num_frames += getJavaTraceAsync(ucontext, frames + num_frames, _max_stack_depth, &java_ctx);
// Lock events and instrumentation events can safely call synchronous JVM TI stack walker.
// Skip Instrument.recordSample() method
int start_depth = event_type == INSTRUMENTED_METHOD ? 1 : 0;
num_frames += getJavaTraceJvmti(jvmti_frames + num_frames, frames + num_frames, start_depth, _max_stack_depth);
}
} else {
// Lock events and instrumentation events can safely call synchronous JVM TI stack walker.
// Skip Instrument.recordSample() method
int start_depth = event_type == INSTRUMENTED_METHOD ? 1 : 0;
num_frames += getJavaTraceJvmti(jvmti_frames + num_frames, frames + num_frames, start_depth, _max_stack_depth);
}

if (num_frames == 0) {
Expand Down
23 changes: 22 additions & 1 deletion src/stackWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth)
CodeBlob* stub = profiler->findRuntimeStub(pc);
const void* start = stub != NULL ? stub->_start : nm->code();
const char* name = stub != NULL ? stub->_name : nm->name();
fillFrame(frames[depth++], BCI_NATIVE_FRAME, name);
fillFrame(frames[depth++], BCI_NATIVE_FRAME, name); // FIXME: BCI_ERROR?

if (frame.unwindStub((instruction_t*)start, name, (uintptr_t&)pc, sp, fp)) {
continue;
Expand Down Expand Up @@ -410,6 +410,27 @@ int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth)
return depth;
}

int StackWalker::flat(void* ucontext, ASGCT_CallFrame* frames) {
const void* pc = ucontext != NULL ? (const void*)StackFrame(ucontext).pc() : __builtin_return_address(0);

if (CodeHeap::contains(pc)) {
NMethod* nm = CodeHeap::findNMethod(pc);
if (nm == NULL) {
fillFrame(frames[0], BCI_ERROR, "unknown_nmethod");
} else if (nm->isNMethod()) {
int level = nm->level();
FrameTypeId type = level >= 1 && level <= 3 ? FRAME_C1_COMPILED : FRAME_JIT_COMPILED;
fillFrame(frames[0], type, 0, nm->method()->id());
} else {
fillFrame(frames[0], BCI_ERROR, nm->name());
}
} else {
fillFrame(frames[0], BCI_ERROR, "native");
}

return 1;
}

void StackWalker::checkFault() {
if (VMThread::key() < 0) {
// JVM has not been loaded or VMStructs have not been initialized yet
Expand Down
1 change: 1 addition & 0 deletions src/stackWalker.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class StackWalker {
static int walkFP(void* ucontext, const void** callchain, int max_depth, StackContext* java_ctx);
static int walkDwarf(void* ucontext, const void** callchain, int max_depth, StackContext* java_ctx);
static int walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth);
static int flat(void* ucontext, ASGCT_CallFrame* frames);

static void checkFault();
};
Expand Down

0 comments on commit 51aec72

Please sign in to comment.