diff --git a/README.md b/README.md index 38380d72..71b21c02 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,8 @@ Example: `./profiler.sh -t 8983` * `-s` - print simple class names instead of FQN. +* `-g` - print method signatures. + * `-a` - annotate Java method names by adding `_[j]` suffix. * `-o fmt[,fmt...]` - specifies what information to dump when profiling ends. diff --git a/profiler.sh b/profiler.sh index 9d07e6bc..38bdd9fd 100755 --- a/profiler.sh +++ b/profiler.sh @@ -18,6 +18,7 @@ usage() { echo " -b bufsize frame buffer size" echo " -t profile different threads separately" echo " -s simple class names instead of FQN" + echo " -g print method signatures" echo " -a annotate Java method names" echo " -o fmt[,fmt...] output format: summary|traces|flat|collapsed|svg|tree|jfr" echo " -v, --version display version string" @@ -146,6 +147,9 @@ while [[ $# -gt 0 ]]; do -s) FORMAT="$FORMAT,simple" ;; + -g) + FORMAT="$FORMAT,sig" + ;; -a) FORMAT="$FORMAT,ann" ;; diff --git a/src/arguments.cpp b/src/arguments.cpp index 6de35d4b..1f3b27c6 100644 --- a/src/arguments.cpp +++ b/src/arguments.cpp @@ -55,8 +55,10 @@ const size_t EXTRA_BUF_SIZE = 512; // threads - profile different threads separately // allkernel - include only kernel-mode events // alluser - include only user-mode events -// simple[=bool] - simple class names instead of FQN -// ann[=bool] - annotate Java method names +// simple - simple class names instead of FQN +// dot - dotted class names +// sig - print method signatures +// ann - annotate Java method names // title=TITLE - FlameGraph title // width=PX - FlameGraph image width // height=PX - FlameGraph frame height @@ -134,9 +136,13 @@ Error Arguments::parse(const char* args) { } else if (strcmp(arg, "alluser") == 0) { _ring = RING_USER; } else if (strcmp(arg, "simple") == 0) { - _simple = value == NULL || strcmp(value, "true") == 0; + _style |= STYLE_SIMPLE; + } else if (strcmp(arg, "dot") == 0) { + _style |= STYLE_DOTTED; + } else if (strcmp(arg, "sig") == 0) { + _style |= STYLE_SIGNATURES; } else if (strcmp(arg, "ann") == 0) { - _annotate = value == NULL || strcmp(value, "true") == 0; + _style |= STYLE_ANNOTATE; } else if (strcmp(arg, "title") == 0 && value != NULL) { _title = value; } else if (strcmp(arg, "width") == 0 && value != NULL) { diff --git a/src/arguments.h b/src/arguments.h index 78667acb..aae648d5 100644 --- a/src/arguments.h +++ b/src/arguments.h @@ -51,6 +51,13 @@ enum Ring { RING_USER }; +enum Style { + STYLE_SIMPLE = 1, + STYLE_DOTTED = 2, + STYLE_SIGNATURES = 4, + STYLE_ANNOTATE = 8 +}; + class Error { private: @@ -88,8 +95,7 @@ class Arguments { int _jstackdepth; int _framebuf; bool _threads; - bool _simple; - bool _annotate; + int _style; const char* _file; bool _dump_collapsed; bool _dump_flamegraph; @@ -115,8 +121,7 @@ class Arguments { _jstackdepth(DEFAULT_JSTACKDEPTH), _framebuf(DEFAULT_FRAMEBUF), _threads(false), - _simple(false), - _annotate(false), + _style(0), _file(NULL), _dump_collapsed(false), _dump_flamegraph(false), diff --git a/src/frameName.cpp b/src/frameName.cpp index b071afb6..956ff3c9 100644 --- a/src/frameName.cpp +++ b/src/frameName.cpp @@ -19,14 +19,13 @@ #include #include #include "frameName.h" +#include "arguments.h" #include "vmStructs.h" -FrameName::FrameName(bool simple, bool annotate, bool dotted, Mutex& thread_names_lock, ThreadMap& thread_names) : +FrameName::FrameName(int style, Mutex& thread_names_lock, ThreadMap& thread_names) : _cache(), - _simple(simple), - _annotate(annotate), - _dotted(dotted), + _style(style), _thread_names_lock(thread_names_lock), _thread_names(thread_names) { @@ -56,31 +55,34 @@ char* FrameName::javaMethodName(jmethodID method) { jclass method_class; char* class_name = NULL; char* method_name = NULL; + char* method_sig = NULL; char* result; jvmtiEnv* jvmti = VM::jvmti(); jvmtiError err; - if ((err = jvmti->GetMethodName(method, &method_name, NULL, NULL)) == 0 && + if ((err = jvmti->GetMethodName(method, &method_name, &method_sig, NULL)) == 0 && (err = jvmti->GetMethodDeclaringClass(method, &method_class)) == 0 && (err = jvmti->GetClassSignature(method_class, &class_name, NULL)) == 0) { // Trim 'L' and ';' off the class descriptor like 'Ljava/lang/Object;' - result = javaClassName(class_name + 1, strlen(class_name) - 2, _simple, _dotted); + result = javaClassName(class_name + 1, strlen(class_name) - 2, _style); strcat(result, "."); strcat(result, method_name); - if (_annotate) strcat(result, "_[j]"); + if (_style & STYLE_SIGNATURES) strcat(result, method_sig); + if (_style & STYLE_ANNOTATE) strcat(result, "_[j]"); } else { snprintf(_buf, sizeof(_buf), "[jvmtiError %d]", err); result = _buf; } jvmti->Deallocate((unsigned char*)class_name); + jvmti->Deallocate((unsigned char*)method_sig); jvmti->Deallocate((unsigned char*)method_name); return result; } -char* FrameName::javaClassName(const char* symbol, int length, bool simple, bool dotted) { +char* FrameName::javaClassName(const char* symbol, int length, int style) { char* result = _buf; int array_dimension = 0; @@ -113,13 +115,13 @@ char* FrameName::javaClassName(const char* symbol, int length, bool simple, bool } while (--array_dimension > 0); } - if (simple) { + if (style & STYLE_SIMPLE) { for (char* s = result; *s; s++) { if (*s == '/') result = s + 1; } } - if (dotted) { + if (style & STYLE_DOTTED) { for (char* s = result; *s; s++) { if (*s == '/') *s = '.'; } @@ -139,14 +141,14 @@ const char* FrameName::name(ASGCT_CallFrame& frame) { case BCI_SYMBOL: { VMSymbol* symbol = (VMSymbol*)frame.method_id; - char* class_name = javaClassName(symbol->body(), symbol->length(), _simple, true); - return strcat(class_name, _dotted ? "" : "_[i]"); + char* class_name = javaClassName(symbol->body(), symbol->length(), _style | STYLE_DOTTED); + return strcat(class_name, _style & STYLE_DOTTED ? "" : "_[i]"); } case BCI_SYMBOL_OUTSIDE_TLAB: { VMSymbol* symbol = (VMSymbol*)((uintptr_t)frame.method_id ^ 1); - char* class_name = javaClassName(symbol->body(), symbol->length(), _simple, true); - return strcat(class_name, _dotted ? " (out)" : "_[k]"); + char* class_name = javaClassName(symbol->body(), symbol->length(), _style | STYLE_DOTTED); + return strcat(class_name, _style & STYLE_DOTTED ? " (out)" : "_[k]"); } case BCI_THREAD_ID: { diff --git a/src/frameName.h b/src/frameName.h index ddb1b970..81006fc3 100644 --- a/src/frameName.h +++ b/src/frameName.h @@ -35,20 +35,18 @@ typedef std::map ThreadMap; class FrameName { private: JMethodCache _cache; - char _buf[520]; - bool _simple; - bool _annotate; - bool _dotted; + char _buf[800]; // must be large enough for class name + method name + method signature + int _style; Mutex& _thread_names_lock; ThreadMap& _thread_names; locale_t _saved_locale; const char* cppDemangle(const char* name); char* javaMethodName(jmethodID method); - char* javaClassName(const char* symbol, int length, bool simple, bool dotted); + char* javaClassName(const char* symbol, int length, int style); public: - FrameName(bool simple, bool annotate, bool dotted, Mutex& thread_names_lock, ThreadMap& thread_names); + FrameName(int style, Mutex& thread_names_lock, ThreadMap& thread_names); ~FrameName(); const char* name(ASGCT_CallFrame& frame); diff --git a/src/javaApi.cpp b/src/javaApi.cpp index 62687d16..a39a4483 100644 --- a/src/javaApi.cpp +++ b/src/javaApi.cpp @@ -98,17 +98,23 @@ Java_one_profiler_AsyncProfiler_dumpCollapsed0(JNIEnv* env, jobject unused, jint extern "C" JNIEXPORT jstring JNICALL Java_one_profiler_AsyncProfiler_dumpTraces0(JNIEnv* env, jobject unused, jint max_traces) { + Arguments args; + args._dump_traces = max_traces ? max_traces : MAX_CALLTRACES; + std::ostringstream out; Profiler::_instance.dumpSummary(out); - Profiler::_instance.dumpTraces(out, max_traces ? max_traces : MAX_CALLTRACES); + Profiler::_instance.dumpTraces(out, args); return env->NewStringUTF(out.str().c_str()); } extern "C" JNIEXPORT jstring JNICALL Java_one_profiler_AsyncProfiler_dumpFlat0(JNIEnv* env, jobject unused, jint max_methods) { + Arguments args; + args._dump_flat = max_methods ? max_methods : MAX_CALLTRACES; + std::ostringstream out; Profiler::_instance.dumpSummary(out); - Profiler::_instance.dumpFlat(out, max_methods ? max_methods : MAX_CALLTRACES); + Profiler::_instance.dumpFlat(out, args); return env->NewStringUTF(out.str().c_str()); } diff --git a/src/profiler.cpp b/src/profiler.cpp index c71ea163..97e0a187 100644 --- a/src/profiler.cpp +++ b/src/profiler.cpp @@ -714,7 +714,7 @@ void Profiler::dumpCollapsed(std::ostream& out, Arguments& args) { MutexLocker ml(_state_lock); if (_state != IDLE || _engine == NULL) return; - FrameName fn(args._simple, args._annotate, false, _thread_names_lock, _thread_names); + FrameName fn(args._style, _thread_names_lock, _thread_names); u64 unknown = 0; for (int i = 0; i < MAX_CALLTRACES; i++) { @@ -743,7 +743,7 @@ void Profiler::dumpFlameGraph(std::ostream& out, Arguments& args, bool tree) { if (_state != IDLE || _engine == NULL) return; FlameGraph flamegraph(args._title, args._counter, args._width, args._height, args._minwidth, args._reverse); - FrameName fn(args._simple, args._annotate, false, _thread_names_lock, _thread_names); + FrameName fn(args._style, _thread_names_lock, _thread_names); for (int i = 0; i < MAX_CALLTRACES; i++) { CallTraceSample& trace = _traces[i]; @@ -771,17 +771,17 @@ void Profiler::dumpFlameGraph(std::ostream& out, Arguments& args, bool tree) { flamegraph.dump(out, tree); } -void Profiler::dumpTraces(std::ostream& out, int max_traces) { +void Profiler::dumpTraces(std::ostream& out, Arguments& args) { MutexLocker ml(_state_lock); if (_state != IDLE || _engine == NULL) return; - FrameName fn(false, false, true, _thread_names_lock, _thread_names); + FrameName fn(args._style | STYLE_DOTTED, _thread_names_lock, _thread_names); double percent = 100.0 / _total_counter; char buf[1024]; qsort(_traces, MAX_CALLTRACES, sizeof(CallTraceSample), CallTraceSample::comparator); - if (max_traces > MAX_CALLTRACES) max_traces = MAX_CALLTRACES; + int max_traces = args._dump_traces < MAX_CALLTRACES ? args._dump_traces : MAX_CALLTRACES; for (int i = 0; i < max_traces; i++) { CallTraceSample& trace = _traces[i]; if (trace._samples == 0) break; @@ -804,21 +804,21 @@ void Profiler::dumpTraces(std::ostream& out, int max_traces) { } } -void Profiler::dumpFlat(std::ostream& out, int max_methods) { +void Profiler::dumpFlat(std::ostream& out, Arguments& args) { MutexLocker ml(_state_lock); if (_state != IDLE || _engine == NULL) return; - FrameName fn(false, false, true, _thread_names_lock, _thread_names); + FrameName fn(args._style | STYLE_DOTTED, _thread_names_lock, _thread_names); double percent = 100.0 / _total_counter; char buf[1024]; qsort(_methods, MAX_CALLTRACES, sizeof(MethodSample), MethodSample::comparator); - if (max_methods > MAX_CALLTRACES) max_methods = MAX_CALLTRACES; snprintf(buf, sizeof(buf), "%12s percent samples top\n" " ---------- ------- ------- ---\n", _engine->units()); out << buf; + int max_methods = args._dump_flat < MAX_CALLTRACES ? args._dump_flat : MAX_CALLTRACES; for (int i = 0; i < max_methods; i++) { MethodSample& method = _methods[i]; if (method._samples == 0) break; @@ -887,8 +887,8 @@ void Profiler::runInternal(Arguments& args, std::ostream& out) { if (args._dump_flamegraph) dumpFlameGraph(out, args, false); if (args._dump_tree) dumpFlameGraph(out, args, true); if (args._dump_summary) dumpSummary(out); - if (args._dump_traces > 0) dumpTraces(out, args._dump_traces); - if (args._dump_flat > 0) dumpFlat(out, args._dump_flat); + if (args._dump_traces > 0) dumpTraces(out, args); + if (args._dump_flat > 0) dumpFlat(out, args); break; default: break; diff --git a/src/profiler.h b/src/profiler.h index 3765d532..6b7a1788 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -197,8 +197,8 @@ class Profiler { void dumpSummary(std::ostream& out); void dumpCollapsed(std::ostream& out, Arguments& args); void dumpFlameGraph(std::ostream& out, Arguments& args, bool tree); - void dumpTraces(std::ostream& out, int max_traces); - void dumpFlat(std::ostream& out, int max_methods); + void dumpTraces(std::ostream& out, Arguments& args); + void dumpFlat(std::ostream& out, Arguments& args); void recordSample(void* ucontext, u64 counter, jint event_type, jmethodID event); NativeCodeCache* jvmLibrary(); const void* findSymbol(const char* name);