6
6
#include " Jit/pyjit.h"
7
7
#include " Jit/threaded_compile.h"
8
8
#include " Jit/util.h"
9
+ #include " pycore_ceval.h"
9
10
10
11
#include < elf.h>
11
12
#include < fcntl.h>
19
20
#include < cstdio>
20
21
#include < cstring>
21
22
#include < ctime>
23
+ #include < iostream>
24
+ #include < regex>
25
+ #include < sstream>
26
+ #include < tuple>
22
27
23
28
#ifdef __x86_64__
24
29
// Use the cheaper rdtsc by default. If you disable this for some reason, or
@@ -242,6 +247,40 @@ void initFiles() {
242
247
inited = true ;
243
248
}
244
249
250
+ // Parses a JIT entry and returns a tuple containing the
251
+ // code address, code size, and entry name. An example of an entry is:
252
+ // 7fa873c00148 360 __CINDER_JIT:__main__:foo2
253
+ std::tuple<const void *, unsigned int , const char *> parseJitEntry (
254
+ const char * entry) {
255
+ std::string_view entry_view = entry;
256
+ size_t space_pos_1 = entry_view.find (' ' );
257
+
258
+ // Extract the hexadecimal code address
259
+ const char * code_addr_str = entry_view.substr (0 , space_pos_1).data ();
260
+ unsigned long long code_addr_val = 0 ;
261
+ std::from_chars (
262
+ code_addr_str, code_addr_str + space_pos_1, code_addr_val, 16 );
263
+ const void * code_addr = reinterpret_cast <const void *>(code_addr_val);
264
+
265
+ // Find the second space character
266
+ size_t space_pos_2 = entry_view.find (' ' , space_pos_1 + 1 );
267
+
268
+ // Extract the hexadecimal code size
269
+ const char * code_size_str =
270
+ entry_view.substr (space_pos_1 + 1 , space_pos_2).data ();
271
+ uint32_t code_size;
272
+ std::from_chars (
273
+ code_size_str,
274
+ code_size_str + (space_pos_2 - space_pos_1 - 1 ),
275
+ code_size,
276
+ 16 );
277
+
278
+ // Extract the entry name
279
+ const char * entry_name = entry_view.substr (space_pos_2 + 1 ).data ();
280
+
281
+ return std::make_tuple (code_addr, code_size, entry_name);
282
+ }
283
+
245
284
// Copy the contents of from_name to to_name. Returns a std::FILE* at the end
246
285
// of to_name on success, or nullptr on failure.
247
286
std::FILE* copyFile (const std::string& from_name, const std::string& to_name) {
@@ -277,6 +316,74 @@ std::FILE* copyFile(const std::string& from_name, const std::string& to_name) {
277
316
}
278
317
}
279
318
319
+ // Copy the contents of the parent perf map file to the child perf map file.
320
+ // Returns 1 on success and 0 on failure.
321
+ int copyJitFile (const std::string& parent_filename) {
322
+ auto parent_file = std::fopen (parent_filename.c_str (), " r" );
323
+ if (parent_file == nullptr ) {
324
+ JIT_LOG (
325
+ " Couldn't open %s for reading (%s)" ,
326
+ parent_filename,
327
+ string_error (errno));
328
+ return 0 ;
329
+ }
330
+
331
+ char buf[1024 ];
332
+ while (std::fgets (buf, sizeof (buf), parent_file) != nullptr ) {
333
+ buf[strcspn (buf, " \n " )] = ' \0 ' ;
334
+ auto jit_entry = parseJitEntry (buf);
335
+ try {
336
+ PyUnstable_WritePerfMapEntry (
337
+ std::get<0 >(jit_entry),
338
+ std::get<1 >(jit_entry),
339
+ std::get<2 >(jit_entry));
340
+ } catch (const std::invalid_argument& e) {
341
+ JIT_LOG (" Error: Invalid JIT entry: %s \n " , buf);
342
+ }
343
+ }
344
+ std::fclose (parent_file);
345
+ return 1 ;
346
+ }
347
+
348
+ // Copy the JIT entries from the parent perf map file to the child perf map
349
+ // file. This is used when perf-trampoline is enabled, as the perf map file
350
+ // will also include trampoline entries. We only want to copy the JIT entries.
351
+ // Returns 1 on success, and 0 on failure.
352
+ int copyJitEntries (const std::string& parent_filename) {
353
+ auto parent_file = std::fopen (parent_filename.c_str (), " r" );
354
+ if (parent_file == nullptr ) {
355
+ JIT_LOG (
356
+ " Couldn't open %s for reading (%s)" ,
357
+ parent_filename,
358
+ string_error (errno));
359
+ return 0 ;
360
+ }
361
+
362
+ char buf[1024 ];
363
+ while (std::fgets (buf, sizeof (buf), parent_file) != nullptr ) {
364
+ if (std::strstr (buf, " __CINDER_" ) != nullptr ) {
365
+ buf[strcspn (buf, " \n " )] = ' \0 ' ;
366
+ auto jit_entry = parseJitEntry (buf);
367
+ try {
368
+ PyUnstable_WritePerfMapEntry (
369
+ std::get<0 >(jit_entry),
370
+ std::get<1 >(jit_entry),
371
+ std::get<2 >(jit_entry));
372
+ } catch (const std::invalid_argument& e) {
373
+ JIT_LOG (" Error: Invalid JIT entry: %s \n " , buf);
374
+ }
375
+ }
376
+ }
377
+ std::fclose (parent_file);
378
+ return 1 ;
379
+ }
380
+
381
+ bool isPerfTrampolineActive () {
382
+ PyThreadState* tstate = PyThreadState_GET ();
383
+ return tstate->interp ->eval_frame &&
384
+ tstate->interp ->eval_frame != _PyEval_EvalFrameDefault;
385
+ }
386
+
280
387
// Copy the perf pid map from the parent process into a new file for this child
281
388
// process.
282
389
void copyFileInfo (FileInfo& info) {
@@ -290,33 +397,53 @@ void copyFileInfo(FileInfo& info) {
290
397
fmt::format (fmt::runtime (info.filename_format ), getpid ());
291
398
info = {};
292
399
293
- unlink (child_filename.c_str ());
294
-
295
- if (_PyJIT_IsEnabled ()) {
296
- // The JIT is still enabled: copy the file to allow for more compilation in
297
- // this process.
298
- if (auto new_pid_map = copyFile (parent_filename, child_filename)) {
299
- info.filename = child_filename;
300
- info.file = new_pid_map;
400
+ if (parent_filename.starts_with (" /tmp/perf-" ) &&
401
+ parent_filename.ends_with (" .map" ) && isPerfTrampolineActive ()) {
402
+ if (!copyJitEntries (parent_filename)) {
403
+ JIT_LOG (
404
+ " Failed to copy JIT entries from %s to %s" ,
405
+ parent_filename,
406
+ child_filename);
301
407
}
302
- } else {
303
- // The JIT has been disabled: hard link the file to save disk space. Don't
304
- // open it in this process, to avoid messing with the parent's file.
305
- if (::link (parent_filename.c_str (), child_filename.c_str ()) != 0 ) {
408
+ } else if (
409
+ parent_filename.starts_with (" /tmp/perf-" ) &&
410
+ parent_filename.ends_with (" .map" ) && _PyJIT_IsEnabled ()) {
411
+ // The JIT is still enabled: copy the file to allow for more compilation
412
+ // in this process.
413
+ if (!copyJitFile (parent_filename)) {
306
414
JIT_LOG (
307
- " Failed to link %s to %s: %s" ,
308
- child_filename,
415
+ " Failed to copy perf map file from %s to %s" ,
309
416
parent_filename,
310
- string_error (errno));
417
+ child_filename);
418
+ }
419
+ } else {
420
+ unlink (child_filename.c_str ());
421
+ if (_PyJIT_IsEnabled ()) {
422
+ // The JIT is still enabled: copy the file to allow for more compilation
423
+ // in this process.
424
+ if (auto new_pid_map = copyFile (parent_filename, child_filename)) {
425
+ info.filename = child_filename;
426
+ info.file = new_pid_map;
427
+ }
311
428
} else {
312
- // Poke the file's atime to keep tmpwatch at bay.
313
- std::FILE* file = std::fopen (parent_filename.c_str (), " r" );
314
- if (file != nullptr ) {
315
- std::fclose (file);
429
+ // The JIT has been disabled: hard link the file to save disk space. Don't
430
+ // open it in this process, to avoid messing with the parent's file.
431
+ if (::link (parent_filename.c_str (), child_filename.c_str ()) != 0 ) {
432
+ JIT_LOG (
433
+ " Failed to link %s to %s: %s" ,
434
+ child_filename,
435
+ parent_filename,
436
+ string_error (errno));
437
+ } else {
438
+ // Poke the file's atime to keep tmpwatch at bay.
439
+ std::FILE* file = std::fopen (parent_filename.c_str (), " r" );
440
+ if (file != nullptr ) {
441
+ std::fclose (file);
442
+ }
316
443
}
444
+ info.file = nullptr ;
445
+ info.filename = " " ;
317
446
}
318
- info.file = nullptr ;
319
- info.filename = " " ;
320
447
}
321
448
}
322
449
@@ -353,19 +480,12 @@ void registerFunction(
353
480
354
481
initFiles ();
355
482
356
- if (auto file = g_pid_map.file ) {
357
- for (auto & section_and_size : code_sections) {
358
- void * code = section_and_size.first ;
359
- std::size_t size = section_and_size.second ;
360
- fmt::print (
361
- file,
362
- " {:x} {:x} {}:{}\n " ,
363
- reinterpret_cast <uintptr_t >(code),
364
- size,
365
- prefix,
366
- name);
367
- std::fflush (file);
368
- }
483
+ for (auto & section_and_size : code_sections) {
484
+ void * code = section_and_size.first ;
485
+ std::size_t size = section_and_size.second ;
486
+ auto jit_entry = prefix + " :" + name;
487
+ PyUnstable_WritePerfMapEntry (
488
+ static_cast <const void *>(code), size, jit_entry.c_str ());
369
489
}
370
490
371
491
if (auto file = g_jitdump_file.file ) {
0 commit comments