Skip to content

Commit 307dc69

Browse files
committed
Merge pull request #35 from jrudolph/w/25-output-all-inlined-frames
add `unfoldall` output mode
2 parents 279a616 + 826a8c3 commit 307dc69

File tree

2 files changed

+85
-11
lines changed

2 files changed

+85
-11
lines changed

README.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ You can add a comma separated list of options to `perf-java` (or the `AttachOnce
6565

6666
- `unfold`: Create extra entries for every codeblock inside a method that was inlined from elsewhere
6767
(named <inlined_method> in <root_method>). Be aware of the effects of 'skid' in relation with unfolding.
68-
See the section below.
68+
See the section below. Also, see the below section about inaccurate inlining information.
69+
- `unfoldall`: Similar to `unfold` but will include the complete inlined stack at a code location in the form
70+
`root_method->inlined method 1->inlined method 2->...->inlined method on top`.
6971
- `unfoldsimple`: similar to `unfold`, however, the extra entries do not include the " in <root_method>" part
7072
- `msig`: include full method signature in the name string
7173
- `dottedclass`: convert class signature (`Ljava/lang/Class;`) to the usual class names with segments separated by dots
@@ -94,6 +96,17 @@ tools that operate on the symbol level like the standard views of `perf report`,
9496
So, while it is tempting to enable unfolded entries for the perceived extra resolution, this extra information is sometimes just noise
9597
which will not only clutter the overall view but may also be misleading or wrong.
9698

99+
### Inaccurate mappings using the `unfold*` options
100+
101+
Hotspot does not retain line number and other debug information for inlined code at other places than safepoints. This
102+
makes sense because you don't usually observe code running between safepoints from the JVM's perspective. This is different
103+
when observing a process from the outside like with `perf`. For observed code locations outside of safepoints, the JVM will
104+
not report any inlining information and perf-map-agent will assign those areas to the host method of the inlining.
105+
106+
For more fidelity, Hotspot can be instructed to include debug information for non-safepoints as well. Use
107+
`-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints` when running the target process. Note, however, that this will
108+
produce a lot more information with the generated `perf-<pid>.map` file potentially growing to MBs of size.
109+
97110
### Agent Library Unloading
98111

99112
Unloading or reloading of a changed agent library is not supported by the JVM (but re-attaching is). Therefore, if you make changes to the

src/c/perf-map-agent.c

+71-10
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,15 @@
3232
#include "perf-map-file.h"
3333

3434
#define STRING_BUFFER_SIZE 2000
35+
#define BIG_STRING_BUFFER_SIZE 20000
3536

3637
bool unfold_inlined_methods = false;
3738
bool unfold_simple = false;
39+
bool unfold_all = false;
3840
bool print_method_signatures = false;
3941
bool print_source_loc = false;
4042
bool clean_class_names = false;
43+
bool debug_dump_unfold_entries = false;
4144

4245
FILE *method_file = NULL;
4346
void open_map_file() {
@@ -140,7 +143,7 @@ void generate_unfolded_entry(jvmtiEnv *jvmti, jmethodID method, char *buffer, si
140143
/* Generates and writes a single entry for a given inlined method. */
141144
void write_unfolded_entry(
142145
jvmtiEnv *jvmti,
143-
jmethodID cur_method,
146+
PCStackInfo *info,
144147
jmethodID root_method,
145148
const char *root_name,
146149
const void *start_addr,
@@ -149,15 +152,60 @@ void write_unfolded_entry(
149152
char inlined_name[STRING_BUFFER_SIZE * 2 + 4];
150153
const char *entry_p;
151154

152-
if (cur_method != root_method) {
153-
generate_unfolded_entry(jvmti, cur_method, inlined_name, sizeof(inlined_name), root_name);
154-
entry_p = inlined_name;
155-
} else
156-
entry_p = root_name;
155+
if (unfold_all) {
156+
char full_name[BIG_STRING_BUFFER_SIZE];
157+
full_name[0] = '\0';
158+
int i;
159+
for (i = info->numstackframes - 1; i >= 0; i--) {
160+
//printf("At %d method is %d len %d remaining %d\n", i, info->methods[i], strlen(full_name), sizeof(full_name) - 1 - strlen(full_name));
161+
sig_string(jvmti, info->methods[i], inlined_name, sizeof(inlined_name));
162+
strncat(full_name, inlined_name, sizeof(full_name) - 1 - strlen(full_name)); // TODO optimize
163+
if (i != 0) strncat(full_name, "->", sizeof(full_name));
164+
}
165+
entry_p = full_name;
166+
} else {
167+
jmethodID cur_method = info->methods[0]; // top of stack
168+
if (cur_method != root_method) {
169+
generate_unfolded_entry(jvmti, cur_method, inlined_name, sizeof(inlined_name), root_name);
170+
entry_p = inlined_name;
171+
} else
172+
entry_p = root_name;
173+
}
157174

158175
perf_map_write_entry(method_file, start_addr, end_addr - start_addr, entry_p);
159176
}
160177

178+
void dump_entries(
179+
jvmtiEnv *jvmti,
180+
jmethodID root_method,
181+
jint code_size,
182+
const void* code_addr,
183+
jint map_length,
184+
const jvmtiAddrLocationMap* map,
185+
const void* compile_info) {
186+
const jvmtiCompiledMethodLoadRecordHeader *header = compile_info;
187+
char root_name[STRING_BUFFER_SIZE];
188+
sig_string(jvmti, root_method, root_name, sizeof(root_name));
189+
printf("At %s size %x from %p to %p", root_name, code_size, code_addr, code_addr + code_size);
190+
if (header->kind == JVMTI_CMLR_INLINE_INFO) {
191+
const jvmtiCompiledMethodLoadInlineRecord *record = (jvmtiCompiledMethodLoadInlineRecord *) header;
192+
printf(" with %d entries\n", record->numpcs);
193+
194+
int i;
195+
for (i = 0; i < record->numpcs; i++) {
196+
PCStackInfo *info = &record->pcinfo[i];
197+
printf(" %p has %d stack entries\n", info->pc, info->numstackframes);
198+
199+
int j;
200+
for (j = 0; j < info->numstackframes; j++) {
201+
char buf[2000];
202+
sig_string(jvmti, info->methods[j], buf, sizeof(buf));
203+
printf(" %s\n", buf);
204+
}
205+
}
206+
} else printf(" with no inline info\n");
207+
}
208+
161209
void generate_unfolded_entries(
162210
jvmtiEnv *jvmti,
163211
jmethodID root_method,
@@ -170,6 +218,10 @@ void generate_unfolded_entries(
170218
char root_name[STRING_BUFFER_SIZE];
171219

172220
sig_string(jvmti, root_method, root_name, sizeof(root_name));
221+
222+
if (debug_dump_unfold_entries)
223+
dump_entries(jvmti, root_method, code_size, code_addr, map_length, map, compile_info);
224+
173225
if (header->kind == JVMTI_CMLR_INLINE_INFO) {
174226
const jvmtiCompiledMethodLoadInlineRecord *record = (jvmtiCompiledMethodLoadInlineRecord *) header;
175227

@@ -185,10 +237,13 @@ void generate_unfolded_entries(
185237

186238
// as long as the top method remains the same we delay recording
187239
if (cur_method != top_method) {
188-
189240
// top method has changed, record the range for current method
190241
void *end_addr = info->pc;
191-
write_unfolded_entry(jvmti, cur_method, root_method, root_name, start_addr, end_addr);
242+
243+
if (i > 0)
244+
write_unfolded_entry(jvmti, &record->pcinfo[i - 1], root_method, root_name, start_addr, end_addr);
245+
else
246+
generate_single_entry(jvmti, root_method, start_addr, end_addr - start_addr);
192247

193248
start_addr = info->pc;
194249
cur_method = top_method;
@@ -199,7 +254,11 @@ void generate_unfolded_entries(
199254
if (start_addr != code_addr + code_size) {
200255
// end_addr is end of this complete code blob
201256
const void *end_addr = code_addr + code_size;
202-
write_unfolded_entry(jvmti, cur_method, root_method, root_name, start_addr, end_addr);
257+
258+
if (i > 0)
259+
write_unfolded_entry(jvmti, &record->pcinfo[i - 1], root_method, root_name, start_addr, end_addr);
260+
else
261+
generate_single_entry(jvmti, root_method, start_addr, end_addr - start_addr);
203262
}
204263
} else
205264
generate_single_entry(jvmti, root_method, code_addr, code_size);
@@ -265,10 +324,12 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
265324
open_map_file();
266325

267326
unfold_simple = strstr(options, "unfoldsimple") != NULL;
268-
unfold_inlined_methods = strstr(options, "unfold") != NULL || unfold_simple;
327+
unfold_all = strstr(options, "unfoldall") != NULL;
328+
unfold_inlined_methods = strstr(options, "unfold") != NULL || unfold_simple || unfold_all;
269329
print_method_signatures = strstr(options, "msig") != NULL;
270330
print_source_loc = strstr(options, "sourcepos") != NULL;
271331
clean_class_names = strstr(options, "dottedclass") != NULL;
332+
debug_dump_unfold_entries = strstr(options, "debug_dump_unfold_entries") != NULL;
272333

273334
jvmtiEnv *jvmti;
274335
(*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1);

0 commit comments

Comments
 (0)