From a3d1db78dc13ddbfe2b4cfbf7e3246fba8a45e1c Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Mon, 28 Jun 2021 12:28:53 -0400 Subject: [PATCH] i#4274 kernel pc: Record interrupted PC before drmemtrace kernel events For online traces, adds the interrupted PC to kernel event entries. For offline traces, updates the existing module offset stored inside kernel events (and previously used only for raw2trace) to become an absolute PC to help in core simulators and other trace consumers who want to know branch targets prior to kernel events. This is a version change for offline traces, and the version is updated, with a named constant for the old version. The raw offline's module offset is upgraded to become an index;offset pair, as that format is better suited for raw2trace and it avoids needing an extra entry nearly all of the time. The raw2trace postprocessing converts it to an absolute PC for the final trace. Since the 49 bits can take two raw entries, raw2trace is augmented to handle unreading such a double entry. Adds a new TRACE_MARKER_TYPE_RSEQ_ABORT marker to further identify an rseq abort, in order to roll back the committing store in raw2trace (previously it used the lack of interrupted-PC to identify an rseq abort). Adds support to the view tool for displaying the interrupted PC depending on the version. Updates the drcachesim documentation with the new output of the interrupted PC from the view tool. Fixes an issue in the reader where the first switch to a thread has the prior thread's identity in the two leading marker entries for version and filetype. Adds tests of the interrupted PC to the trace_invariants test for signals, as well as ensuring that raw2trace has rolled back an rseq abort final instruction so that the abort appears to occur at a legitimate place inside the region. Adds a test of the legacy version with just offsets by checking in raw files from a signal_invariants run, with fake libdrmemtrace.so and lidynamorio.so to keep the size down. Fixes #4274 --- api/docs/release.dox | 3 + clients/drcachesim/common/trace_entry.h | 47 ++++++- clients/drcachesim/drcachesim.dox.in | 99 ++++++------- clients/drcachesim/reader/file_reader.h | 5 +- clients/drcachesim/reader/reader.cpp | 12 +- .../drmemtrace.signal_invariants | Bin 0 -> 21800 bytes .../libdrmemtrace.so | Bin 0 -> 9120 bytes .../libdynamorio.so | Bin 0 -> 9120 bytes ...trace.signal_invariants.552306.6024.raw.gz | Bin 0 -> 51161 bytes ...trace.signal_invariants.552323.7114.raw.gz | Bin 0 -> 2861 bytes ...trace.signal_invariants.552324.5662.raw.gz | Bin 0 -> 2498 bytes .../raw/funclist.log | 0 .../raw/modules.log | Bin 0 -> 12709 bytes .../tests/offline-legacy-int-offs.templatex | 66 +++++++++ clients/drcachesim/tests/trace_invariants.cpp | 48 +++++-- clients/drcachesim/tests/trace_invariants.h | 2 + clients/drcachesim/tools/view.cpp | 22 ++- clients/drcachesim/tracer/instru.h | 2 +- clients/drcachesim/tracer/instru_offline.cpp | 4 +- clients/drcachesim/tracer/raw2trace.cpp | 26 +++- clients/drcachesim/tracer/raw2trace.h | 132 +++++++++++++----- clients/drcachesim/tracer/tracer.cpp | 45 ++++-- suite/tests/CMakeLists.txt | 16 +++ 23 files changed, 403 insertions(+), 126 deletions(-) create mode 100755 clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/drmemtrace.signal_invariants create mode 100755 clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/libdrmemtrace.so create mode 100755 clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/libdynamorio.so create mode 100644 clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/drmemtrace.drmemtrace.signal_invariants.552306.6024.raw.gz create mode 100644 clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/drmemtrace.drmemtrace.signal_invariants.552323.7114.raw.gz create mode 100644 clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/drmemtrace.drmemtrace.signal_invariants.552324.5662.raw.gz create mode 100644 clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/funclist.log create mode 100644 clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/modules.log create mode 100644 clients/drcachesim/tests/offline-legacy-int-offs.templatex diff --git a/api/docs/release.dox b/api/docs/release.dox index f8c9224094e..9c2b98ab3f4 100644 --- a/api/docs/release.dox +++ b/api/docs/release.dox @@ -225,6 +225,9 @@ Further non-compatibility-affecting changes include: - Added the source context for restartable sequence aborts (#DR_XFER_RSEQ_ABORT) which was previously missing. - Added a #TRACE_MARKER_TYPE_VERSION entry to drmemtrace traces. + - Augmented drmemtrace #TRACE_MARKER_TYPE_KERNEL_EVENT entries with the absolute + PC of the interruption point, including for restartable sequence aborts, which + now also have an additional #TRACE_MARKER_TYPE_RSEQ_ABORT identifier. **************************************************
diff --git a/clients/drcachesim/common/trace_entry.h b/clients/drcachesim/common/trace_entry.h index db975cd3a60..4119f7cf850 100644 --- a/clients/drcachesim/common/trace_entry.h +++ b/clients/drcachesim/common/trace_entry.h @@ -59,7 +59,14 @@ typedef uintptr_t addr_t; /**< The type of a memory address. */ * #TRACE_MARKER_TYPE_VERSION. */ typedef enum { - TRACE_ENTRY_VERSION = 2 /**< The latest version of the trace format. */ + /** + * A prior version where #TRACE_MARKER_TYPE_KERNEL_EVENT provided the module + * offset (and nothing for restartable sequence aborts) rather than the absolute + * PC of the interruption point provided today. + */ + TRACE_ENTRY_VERSION_NO_KERNEL_PC = 2, + /** The latest version of the trace format. */ + TRACE_ENTRY_VERSION, } trace_version_t; /** The type of a trace entry in a #memref_t structure. */ @@ -208,10 +215,21 @@ typedef enum { * The subsequent instruction is the start of a handler for a kernel-initiated * event: a signal handler or restartable sequence abort handler on UNIX, or an * APC, exception, or callback dispatcher on Windows. - * The value holds the module offset of the interruption point PC, - * which is used in post-processing. The value is 0 for some types, namely - * Windows callbacks and Linux rseq aborts, but these can be assumed to target - * the start of a block and so there is no loss of accuracy when post-processing. + * The value of this marker contains the program counter at the kernel + * interruption point. If the interruption point is just after a branch, this + * value is the target of that branch. + * (For trace version #TRACE_ENTRY_VERSION_NO_KERNEL_PC or below, the value is + * the module offset rather than the absolute program counter.) + * The value is 0 for some types where this information is not available, namely + * Windows callbacks. + * A restartable sequence abort handler is further identified by a prior + * marker of type #TRACE_MARKER_TYPE_RSEQ_ABORT. + */ + /* Non-exported information since limited to raw offline traces: + * For raw offline traces, the value is in the form of the module index and offset + * (from the base, not the indexed segment) of type kernel_interrupted_raw_pc_t. + * For raw offline traces, a value of 0 can be assumed to target the start of a + * block and so there is no loss of accuracy when post-processing. */ TRACE_MARKER_TYPE_KERNEL_EVENT, /** @@ -301,6 +319,12 @@ typedef enum { */ TRACE_MARKER_TYPE_VERSION, + /** + * Serves to further identify #TRACE_MARKER_TYPE_KERNEL_EVENT as a + * restartable sequence abort handler. + */ + TRACE_MARKER_TYPE_RSEQ_ABORT, + // ... // These values are reserved for future built-in marker types. // ... @@ -420,7 +444,8 @@ typedef enum { #define OFFLINE_FILE_VERSION_NO_ELISION 2 #define OFFLINE_FILE_VERSION_OLDEST_SUPPORTED OFFLINE_FILE_VERSION_NO_ELISION #define OFFLINE_FILE_VERSION_ELIDE_UNMOD_BASE 3 -#define OFFLINE_FILE_VERSION OFFLINE_FILE_VERSION_ELIDE_UNMOD_BASE +#define OFFLINE_FILE_VERSION_KERNEL_INT_PC 4 +#define OFFLINE_FILE_VERSION OFFLINE_FILE_VERSION_KERNEL_INT_PC /** * Bitfields used to describe the high-level characteristics of both an @@ -513,6 +538,16 @@ struct _offline_entry_t { } END_PACKED_STRUCTURE; typedef struct _offline_entry_t offline_entry_t; +// This is the raw marker value for TRACE_MARKER_TYPE_KERNEL_*. +// It occupies 49 bits and so may require two raw entries. +typedef union { + struct { + uint64_t modoffs : PC_MODOFFS_BITS; + uint64_t modidx : PC_MODIDX_BITS; + } pc; + uint64_t combined_value; +} kernel_interrupted_raw_pc_t; + /** * The name of the file in -offline mode where module data is written. * Its creation can be customized using drmemtrace_custom_module_data() diff --git a/clients/drcachesim/drcachesim.dox.in b/clients/drcachesim/drcachesim.dox.in index f80e0583ffc..ef6dcb3408a 100644 --- a/clients/drcachesim/drcachesim.dox.in +++ b/clients/drcachesim/drcachesim.dox.in @@ -598,49 +598,48 @@ T342626 write 8 byte(s) @ 0x7f899f928e70 \endcode -Here is an example of a signal handler interrupting the regular flow: +Here is an example of a signal handler interrupting the regular flow, +with metadata showing that the signal was delivered just after a +non-taken conditional branch: \code -T342625 0x0000000000469957 49 8b 44 17 18 mov 0x18(%r15,%rdx), %rax -T342625 read 8 byte(s) @ 0x4e7848 -T342625 0x000000000046995c 48 85 c0 test %rax, %rax -T342625 0x000000000046995f 0f 84 a5 00 00 00 jz $0x0000000000469a0a -T342625 -T342625 -T342625 -T342625 0x000000000040257d 55 push %rbp -T342625 write 8 byte(s) @ 0x7ffe70dcda30 -T342625 0x000000000040257e 48 89 e5 mov %rsp, %rbp -T342625 0x0000000000402581 89 7d fc mov %edi, -0x04(%rbp) -T342625 write 4 byte(s) @ 0x7ffe70dcda2c -T342625 0x0000000000402584 48 89 75 f0 mov %rsi, -0x10(%rbp) -T342625 write 8 byte(s) @ 0x7ffe70dcda20 -T342625 0x0000000000402588 48 89 55 e8 mov %rdx, -0x18(%rbp) -T342625 write 8 byte(s) @ 0x7ffe70dcda18 -T342625 0x000000000040258c 83 7d fc 1a cmp -0x04(%rbp), $0x1a -T342625 read 4 byte(s) @ 0x7ffe70dcda2c -T342625 0x0000000000402590 75 0f jnz $0x00000000004025a1 -T342625 0x0000000000402592 8b 05 5c 0f 0e 00 mov 0x00000000004e34f4, %eax -T342625 read 4 byte(s) @ 0x4e34f4 -T342625 0x0000000000402598 83 c0 01 add $0x01, %eax -T342625 0x000000000040259b 89 05 53 0f 0e 00 mov %eax, 0x00000000004e34f4 -T342625 write 4 byte(s) @ 0x4e34f4 -T342625 0x00000000004025a1 90 nop -T342625 0x00000000004025a2 5d pop %rbp -T342625 read 8 byte(s) @ 0x7ffe70dcda30 -T342625 0x00000000004025a3 c3 ret -T342625 read 8 byte(s) @ 0x7ffe70dcda38 -T342625 0x0000000000407bb0 48 c7 c0 0f 00 00 00 mov $0x0000000f, %rax -T342625 0x0000000000407bb7 0f 05 syscall -T342625 -T342625 -T342625 -T342625 -T342625 -T342625 0x0000000000469965 49 8b 54 17 10 mov 0x10(%r15,%rdx), %rdx -T342625 read 8 byte(s) @ 0x4e7840 -T342625 0x000000000046996a 48 3b 15 9f fa 07 00 cmp %rdx, 0x00000000004e9410 -T342625 read 8 byte(s) @ 0x4e9410 +T585061 0x00007fdb4e95128f 41 f6 44 24 08 08 test 0x08(%r12), $0x08 +T585061 read 1 byte(s) @ 0x7ffd5af76b08 +T585061 0x00007fdb4e951295 0f 85 28 04 00 00 jnz $0x00007fdb4e9516c3 +T585061 +T585061 +T585061 +T585061 0x00007fdb4ace9dba 55 push %rbp +T585061 write 8 byte(s) @ 0x7ffd5af763d0 +T585061 0x00007fdb4ace9dbb 48 89 e5 mov %rsp, %rbp +T585061 0x00007fdb4ace9dbe 89 7d fc mov %edi, -0x04(%rbp) +T585061 write 4 byte(s) @ 0x7ffd5af763cc +T585061 0x00007fdb4ace9dc1 48 89 75 f0 mov %rsi, -0x10(%rbp) +T585061 write 8 byte(s) @ 0x7ffd5af763c0 +T585061 0x00007fdb4ace9dc5 48 89 55 e8 mov %rdx, -0x18(%rbp) +T585061 write 8 byte(s) @ 0x7ffd5af763b8 +T585061 0x00007fdb4ace9dc9 83 7d fc 1a cmp -0x04(%rbp), $0x1a +T585061 read 4 byte(s) @ 0x7ffd5af763cc +T585061 0x00007fdb4ace9dcd 75 0f jnz $0x00007fdb4ace9dde +T585061 0x00007fdb4ace9dcf 8b 05 7f 23 20 00 mov 0x00007fdb4aeec154, %eax +T585061 read 4 byte(s) @ 0x7fdb4aeec154 +T585061 0x00007fdb4ace9dd5 83 c0 01 add $0x01, %eax +T585061 0x00007fdb4ace9dd8 89 05 76 23 20 00 mov %eax, 0x00007fdb4aeec154 +T585061 write 4 byte(s) @ 0x7fdb4aeec154 +T585061 0x00007fdb4ace9dde 90 nop +T585061 0x00007fdb4ace9ddf 5d pop %rbp +T585061 read 8 byte(s) @ 0x7ffd5af763d0 +T585061 0x00007fdb4ace9de0 c3 ret +T585061 read 8 byte(s) @ 0x7ffd5af763d8 +T585061 0x00007fdb4e95c140 48 c7 c0 0f 00 00 00 mov $0x0000000f, %rax +T585061 0x00007fdb4e95c147 0f 05 syscall +T585061 +T585061 +T585061 +T585061 +T585061 +T585061 0x00007fdb4e95129b 48 8b 1d 8e 40 01 00 mov 0x00007fdb4e965330, %rbx +T585061 read 8 byte(s) @ 0x7fdb4e965330 \endcode \section sec_tool_func_view View Function Calls @@ -1050,13 +1049,6 @@ instruction information to go along with each load and store, while cache simulators can ignore these "no-fetch" entries and avoid incorrectly inflating instruction fetch statistics. -Offline traces guarantee that a branch target instruction entry in a -trace must immediately follow the branch instruction with no intervening -thread switch. This allows a core simulator to identify the target of a -branch by looking at the subsequent trace entry. However, this guarantee -does not hold when a kernel event such as a signal is delivered -immediately after a branch. - Traces include scheduling markers providing the timestamp and hardware thread identifier on each thread transition, allowing a simulator to more closely match the actual hardware if so desired. @@ -1064,6 +1056,14 @@ closely match the actual hardware if so desired. Traces also include markers indicating disruptions in user mode control flow such as signal handler entry and exit. +Offline traces guarantee that a branch target instruction entry in a +trace must immediately follow the branch instruction with no intervening +thread switch. This allows a core simulator to identify the target of a +branch by looking at the subsequent trace entry. This guarantee +does not hold when a kernel event such as a signal is delivered +immediately after a branch; however, each marker indicating such a kernel transfer +includes the interrupted PC, explicitly providing the branch target. + Filtered traces (filtered via -L0_filter) include the dynamic (pre-filtered) per-thread instruction count in a #TRACE_MARKER_TYPE_INSTRUCTION_COUNT marker at each thread buffer boundary and at thread exit. @@ -1248,6 +1248,9 @@ these areas of missing functionality: - Online traces may skip instructions immediately prior to non-load-or-store-related kernel transfer events (https://github.com/DynamoRIO/dynamorio/issues/3937). +- Online traces may include the committing store in the trace when a + restartable sequence abort happened prior to that store + (https://github.com/DynamoRIO/dynamorio/issues/4041). - Application phase marking is not yet implemented (https://github.com/DynamoRIO/dynamorio/issues/2478). diff --git a/clients/drcachesim/reader/file_reader.h b/clients/drcachesim/reader/file_reader.h index 6089b1cbc44..ab8a990906c 100644 --- a/clients/drcachesim/reader/file_reader.h +++ b/clients/drcachesim/reader/file_reader.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2016-2020 Google, Inc. All rights reserved. + * Copyright (c) 2016-2021 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -168,7 +168,8 @@ template class file_reader_t : public reader_t { return false; } // We can handle the older version 1 as well which simply omits the - // early marker with the arch tag. + // early marker with the arch tag, and version 2 which only differs wrt + // TRACE_MARKER_TYPE_KERNEL_EVENT.. if (header.addr > TRACE_ENTRY_VERSION) { ERRMSG( "Cannot handle version #%zu (expect version <= #%u) for input file " diff --git a/clients/drcachesim/reader/reader.cpp b/clients/drcachesim/reader/reader.cpp index 2f2506fc0c9..7978703d51a 100644 --- a/clients/drcachesim/reader/reader.cpp +++ b/clients/drcachesim/reader/reader.cpp @@ -204,9 +204,15 @@ reader_t::operator++() case TRACE_TYPE_MARKER: have_memref = true; cur_ref_.marker.type = (trace_type_t)input_entry_->type; - assert((cur_tid_ != 0 && cur_pid_ != 0) || - input_entry_->size == TRACE_MARKER_TYPE_VERSION || - input_entry_->size == TRACE_MARKER_TYPE_FILETYPE); + if (input_entry_->size == TRACE_MARKER_TYPE_VERSION || + input_entry_->size == TRACE_MARKER_TYPE_FILETYPE) { + // Do not carry over a prior thread on a thread switch to a + // first-time-seen new thread, whose tid entry is *after* these + // 2 markers. + cur_pid_ = 0; + cur_tid_ = 0; + } else + assert(cur_tid_ != 0 && cur_pid_ != 0); cur_ref_.marker.pid = cur_pid_; cur_ref_.marker.tid = cur_tid_; cur_ref_.marker.marker_type = (trace_marker_type_t)input_entry_->size; diff --git a/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/drmemtrace.signal_invariants b/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/drmemtrace.signal_invariants new file mode 100755 index 0000000000000000000000000000000000000000..4d372e5e9d34e08719a00192d353942309822a6a GIT binary patch literal 21800 zcmeHPeRx#Gxt~o~hfHz?Ay(l!d#T@p2=P=X?RfA4(ko?Ui* zdizJ)KNvW9=lz_Scix#ZbN0;Xbkts*W3e!ma@iLd$0iC&rDPPZR1%C;vc+s1{@U3z zHX7tnxGE((9k%2MhJtxgE*FsWo|61@I919isA@=%^s=R>O>h)s1p+5MD`h2~4%-Ta zoPvsmBpz>AQYq`Npe&x1 zwYt|YT2R*7G`H3532d6XY4M`Dix!mny`}ScyCPQhD<9W%T$kZev573Z02le= zVq8?#rMPC`x)2wI*7>;7(?yan1#lWJs)ISr1;S(2OhjIWP8yZ5;xj0MAcq8-0CWs4 z8M2%?4u5Cy9Ak3;&%<>tF7jtNt}oyspULR}xn!E1%<|k={2h;rVzLC+WL%WWa8aGf zALMVU!@h#9%7W>W^76)8!-b<*CGn{KBs+TI)E_N(yghWsIV%qAJoL~#mu$X;?Q^}+ zHh$@2XI+?qxHlPvPbv(OL1jvc)8^eYfD zwd=er<^C><{9Re-PiLY3JPV!Xr%d&ko`wEG7CTxNdRrDd&t;LPdBBb8VW&X_Fp zM`0jSyJ%2mqBmucUkG^`*XikLPM^Zo-6%L}4*4!04<7AZfTCUT_LfK&RGq_2|>lVr9-(kILH#0eqJ zSVPUKl?_cUpKF8Lue*G!R<3CEdR(iV>swudmzmSx^7%a8hE{K*Q+InktX*&MxtvW6 zcX-`Nq!A?DmEg4nbk`&+fkMpb_q%+0L$lM}%G%nT?X215ZD!37 z@-x5g!+!`t8=7~twX-%?TVwlX*4)_Y^}ASGyU(k;8g()c&ukzGzYd4_SX;nD)*2cb zH#r-c-5zJF`)(Im1k+14u{LOI@as+#-sXf~Ha55W+#VfO^~ek)q~~bKP|iq$a*LF9 zqd!2V-K^a$JlEiAa_UZoS~a@-sK0vy%57+IdYW2YR3>pnnLswQc|EcM4d~4qkZ*E( z8UlV-leqxy;0Bk6i0l1+sow5p*VNW5U(qnX^om4o-hxDSe(6FAq8xtj%)y;B{U6<3 zN8uu#RdR(KaRi-kOX8n7uc#tWEs*;ZkC9VtHZY&$D>=Hgc7<>2|k7AiKiWN&d zFvX4KL@v7=I3=>yDfI~#{G0?|t$)=#Fd)ZauUzLU`gS$BrOg{m;(a%eb+P6$uZQ|g zbXwb}G+?6B8cwCCiLTaT#2YrzX$_>(h>1R35djP3d`N2>G(VB-Cc1PH=M|giYR)1? zxruIG`zg1 z2)M~aFEG)!nCO_r6X^jHU4;TMyG(RitE#lkM5nc@O508J$%+X0l!`b>25{-NJQr?rDh1137{KU9jE=(KlGY1l-!E8^!s zEjW~?g{*I1#hA7=qUXf>wBYW%j^I)e1PL$CY(Yy*2nRi2&a&ZML2#f;S{Q|=QzHca0=1bc8)J4oI*3! z#qs%sQ%J_PaC{cw6pFESj?W;RLNK zxeGW?^FFLG_T8=-?`grq!*#1_!`5q2wicdn2vyR;6;bp=eDbrONd=XMiGv3$7YoUtephxGTN1`qGv&VWPaLZbyr(oLsdW)edmuFU>%9C*hj0=M{ocD>_g!93zk((JJB}$KxBv z@FnNqDlJq`WEgps17nY7{5o0&g{_gm6mY4C5zRQD8GECJAk&hN4Nm34;YrNI{RCmd zt-jVHt5~4zb&Sx>S4i!RR-u0PMf3`e?~78M2EU+~GrB{B{SGZmLhnT0F-^u@t5g&_-oOp3~Gp^?yxHFxAxsyUMG>k3iD>wC2j+9B&%Ooq+9 zTS)^#HWXn$PA{Xsop<5yD19CNjt$%i!%--5lP zw%fa#ZN(#?zjM@f?%KY=8Y%KC64}8;){w{tM{T$4288M@Fi~KASn#;1ZPL_H+Zsn7 z>^S;@JvqF)H97h$CIQ8`Pcmq=5q-qOxKlD{*%Q47 z4Am(YQ#FcF4NSe(=#3CloLu7ME6z10PV5SBj-8M?OTpP=eFD;!Xe~*JN>&6(>i3Y+ z!ib3Qgg5zZee!e!rw~Cemi>hrxfCLp11IbhBBwDR5+dWdh+c#!K!8)T;k<7`P`ITC z3$s^HDvPe5qA2*nxB-frUxBFO1t7zGf_yTQ$u$X+Z(_wEOb$aZ`t#$-sJj6KEo6OQ z5kgI~c_OIFV$$OkcpQ4XQm^&c3da`_E%p_RSzF=MB3gcSb=k&sb#;L`wTPCdgUmLL zBeeFk6;9$p+TK0OfojGFb^o!ZN820WSSU~3pY432{;QS{uVPJ#;VVYuU$cZ5fZeUqbwhxTvxDmi^DSgvdvaabIL!LVSz|927f$`x4?dj745R*t$k!#S-GWB>fsi zrzOPLBz?M~|NWK_zs7bzjGy-rnLIc64_rb_lab1o5Em+hmJo+9>ycXrX$i4BQR_0) zn)>T+SVBxn7&(GL2qOcqkGmZ$Gg6iiHy_1VKf8pWeSy(&D&#m792m1UWg0R9heM9T zn8y3DAjdifv(g#6n#6|Y;bFK_F5=?>8R`^s$w6mNEJ8!|17uB3;}`x3=23pfDW*?} zj)S2P>^`SNJ=mzp%6*J&j2L70 zVs`9Gxx-USN3bFrHtL6rmC;cB5P}Z@h{;+Qyc`~t?V)|6(J>Tq3~Ad^-CL<{{A$x! zj?zEZ$M+5mpe~y6axvdRy|9LZN9Z|*W`t=QTWf4>=ODZZ0J}81t;Dg#R`QCB^|?iw=RYxJrCKtbs2KyR6^K+)#+Ax{-K4p))6qaY6L>o zT>PRUkNj=iy+-;oQzKd$y7h7~>G4b7W<MgY#ZI^s4M1>Q&_079j+OvGI}**pYiH0TJ7)^lxSQt@MJL4?pzj) zod?@|9*POqSq${!wmsF>=-d-)4~85EY0{rb<)K#YH#amlH*4KT?A@Q_bdO9kx{bb) zUPw^Q>5krc07eYQv5@0faAf?(4)pOVJVD#}py=Z+JY7Q{9|wpo8^Q>!Kd3dns<|V= zY}DR8l4Bg`K0U2u7^Zu>2XZXEmWZ*h1V}Ja9&9^hp+S!dKCsFPw?I34r4CZ7I-fvQ zM0-%O=z`}lcO>1AaOg|a3D_p?qBY?wm*S7`S~>8fc%_;fjrxPe%KmD@vA@<>*$YSF zcF9n;YD{J!Z1X}Z2PjPAf! zo3(p~$gc5nErLliUZAsjNeoleMa}r--sXx(G!DkMJ_f!qCrRSj9{;$pSDC>UZ%+j4-4Q# zdJ>yQ+xl>r#!zT0RZwiOj5kpJw^2Z{{0>$AepHLfZ#|>@0a5-qQGO6Wlz#`x?-I8K z%{UoFvFyvnwbmV*Rxg;ygCgD=?+)St+6kk#M>9sWctikTdm}K)dbIGUnc8D<^tgaG zYsQ~RGWaRe7vQNNG8TO{-BW^}=IAqOjaQM$)i10yUPES-UQipVo{28{)T&!+L$xyt zYm8sothe^ObEsv@G;B(qoir1h5EwN66gsro#lg5ZJot~7uY)OgA;9?g@Y4podtt=w$8l4Tw5o_9_wi?wN1N^ zTFk1xR2#1NSrIa;Ym5U`^)<%vs#R6Ssd_Cu_bHHXt}XeDej|v!^#L5*{aLPl(Y$wT z(^#!>wAT2j+W4RhqK)qrQPtOsR~e(J9Zyy&xk z&U)ai2hMuntOw3|;H(G!K_0O1w~FvSp0~+mZ*^|=26Q{#;|jF3(+gtujc#wNc=7CN zyw&Auvg=-Zqqlvty*c1%#Dg<2Q9m0}>Wgauk35PJVRIn{Re> zka#z4DR*v?DBqunWo_DRK}@_CC|H_a8m zUId5D+JuW>z+SYVe90m|yW7mrs1{v5Cg@YzGK z1Ng1O@i?7Lx$*sYycbaWV>~_t*c6S&$75qX2K$XUfR6#z0{#lH74UWJM;-(`4fqV; zpRhlA9q=Ibzr%oofCbq5RN}!vIiLYp2S`s>bii8x9|rsZ;7p8%}JzEr&{Wx4w% zX4zC=xoG^Dye`X_0;1FVNdxfbY(SMV%ZqapB;WT=Jl+TYviyRp^9!%Fjk_yv3%hE{ zm9s9NIfHDFel@O5uuBiER3hEmaMk}l9{)ZOtMUu(&si~Hbk38gk*Lc(AoqjcmBJ4t z_)mbYp}!ZH`CiWEZTSW0w}H=(J0mNl=C(j$Drl#0-2ncd&HS8? zxV{~eW`Mb!< zM(%D4Nl-n10Dc|fHJcx49J~d7Hb2t%Sb;dt=EqTp|1W``&5tv|UySWyHa}K_e=Ydg z{MZEk#TZ|o=f?*ia~LvsG?$E(Ym+{rG5Z$y^=5uf9Ty-U(OAyISglFnS987{v?%!V z!2h0^Pks4VvW)Q<$In8h%q(+9l0OIhm%+cz%+I+xQHNUaqu}!&TU9^iyp-U#f`32e z5Bga*F&=WSN%ZrB;6IOf1y6L7e0Q?^XTXnuf32CH<4D+l9sDEU)7gWh{hWmTA@KKM zp32s~@tB7Oz`r%c{_2GNIpAM}c`aKU)q+otP-#9$l;0`pKNF-@@NWixqPc!rFgMrA zt4LaWE}?mHG3Lj$L|C3*@b#RU{KBv0I`Zw|QI7oL2d&ll<@b-)@)rlkT$5jUNB-if z{PL>&;^q1F<@tq>U!I@G-*-L;J9V%_#hp!OJ#f|oXFc%G?g4dvLYTCpF z=;BF}4OjGb=@`|Hekn&k?@%fffZ<1J-X5S_6hA6)yg=&5C9dj$Wjg0SB^!cQ(m0-| zx3v3@0*Q0wX%$!`p*Te~yTbNRaTO!-kvjW;uoPJ^j&mr5U<7;mZcG2$eJnTUKYLS4nL znU#x^0_B1PpR~AOpUu#ItGu7hgA9mX3t2^XT$0J2*q5S4`mb07DPT5#?}00 z=H{Btji%BkGW8vVR61T>;)_``mCBuzm@mzoT+CK#o=l~m&oYg>Tzo}9ooh>#$Lv#Q zrigrQhI6vS$;EkFyNqkIRxVSsxtU7xY8Fq@>2^WqZPU-UlG`)5n14Hf&iSsBANMGoAl!gHR?r!7TJgLC|f34 zHkN)K{JJdi>$1>&S?HlGbXw13I!FKgEb>1E-JagLz~9Ise<%z6(=7BUSisE4V1FU# znfylIyU9e~06NVnXC!&5J9#c!9()&P-7D3JX*wL8II^8~SUyGQ^UmMn4lWMFr)Y@7 z?I|RAC|n#_PvOvc_Y{IUdY(#5oNZ4bryQM6;bc92&YyxOkG!WS@e}tcl$7J@bSgiM zn{s{~-jL_}Q>4Uk{R9aI)YA^S(^mizY~C-j%f#0O#K#9x1WbKw`u0GYNXo|t61*$e z8RzK5Paz2jssaBRfcmmP3WtB%AcbQ7vO)?&eN7;Rh(r7IiG&my53!7&aZukXNEJ_h z3n7KBDw6ytLkc_fGYBax@sWiTQmQff{6h*`d|@Gl#7g~}+jQr8K;0)`i^|~#7niS{ zmEtEhSLp^%pp-9==i(C(f>gD{)QeAxnj`Ij0>eO~^FhEi8cL$lA>=4xnZf)RxY)dr`} z=iDqbRG$C5PbpnrP#L=(8QMZCef7LR!ELf2@)1=< zm9O^gt3ku9CZVsMKPaf4H$a4^bpPKDD(&r6`D#C;;CiX2>aXM!qzIyC1qxUDCI#0? zebvXR{*uh@MuwWG^ws`KLG`>sm9O+w`|p$b%cViJ4^wcvG)(%Wldk_Y(8wpsJ{DOb zNvNKyNNS?r6>cD7*0)PN1^I7=N*FNBJbO4zU+w=CRP|LR6s{nx`_1Ji79(;|;yhnz z3aH9g)bFS1tNo;c>N$~;m*J>T_O#Sj{inWHq+oY~Aym@K|8bhW+Mg;&`vP-HFQ1OJ zlSrZ%wU3q1t{%)uBg${e=4;5{m2N(Uh^C$os_*!v%PU;Lx1od8cS2w7i+d}`S~A`Q zmc*6*yI@f4(HN$r_Rs1&iK<~VekdtD<;QKHQTr46H;@$U$Tv931)BJ)^c6e;j#)pk zAFmWDBMD8BQF;o}Z=Gg+wND$V6!NN`0!!9k;U|$HTdMwQf1g(+B-W8}Q?i@#ls%1$ zM9yfZ)aSn;lAuh=oPxA}Re2>taB-Euip@mi)8mhH6R~HKT_^Py%Z|GQyo+%uzp43) j#+PclNu6!e1a@~CGrg{~$4C?5iW6&v{`qMNX(;keX)Uyg)q|ji4b8!e3#CO$53)j9=OCqr3LeyTmn3aro7LNBTXI|95R0{J)FGc%nt`DO3noj4U*P}zT=Y_-V~gr}&GKKe?V+++lvL8G>nKvIhnF9@VV7)H@OSf_me&kN$%ixa<@fLE*fiI32z0yy#Z zX5t5kNmJpvX*Hsx=BzbZPSDy2tCu#a;3PDSBhztNe`TceC>GpdX=eY;I|}n&LU+se z5~fW^I?|rYHycElebMhe2Jbtl4{c1rpq|4P<>SsNGy2-f9lZ0<-RXSFHEY))5{kC+ xRww|C{zuXGZ}QI(iFOLhU~b8%>TXHl(lL&jdbk058e7BipXq%1o{n2O|0fy0d`18O literal 0 HcmV?d00001 diff --git a/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/libdynamorio.so b/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/libdynamorio.so new file mode 100755 index 0000000000000000000000000000000000000000..aa2effca2609a6f495b97f812bc40cb4eef3f752 GIT binary patch literal 9120 zcmeHN&ubGw6n>keX)Uyg)q|ji4b8!e3#CO$53)j9=OCqr3LeyTmn3aro7LNBTXI|95R0{J)FGc%nt`DO3noj4U*P}zT=Y_-V~gr}&GKKe?V+++lvL8G>nKvIhnF9@VV7)H@OSf_me&kN$%ixa<@fLE*fiI32z0yy#Z zX5t5kNmJpvX*Hsx=BzbZPSDy2tCu#a;3PDSBhztNe`TceC>GpdX=eY;I|}n&LU+se z5~fW^I?|rYHycElebMhe2Jbtl4{c1rpq|4P<>SsNGy2-f9lZ0<-RXSFHEY))5{kC+ xRww|C{zuXGZ}QI(iFOLhU~b8%>TXHl(lL&jdbk058e7BipXq%1o{n2O|0fy0d`18O literal 0 HcmV?d00001 diff --git a/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/drmemtrace.drmemtrace.signal_invariants.552306.6024.raw.gz b/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/drmemtrace.drmemtrace.signal_invariants.552306.6024.raw.gz new file mode 100644 index 0000000000000000000000000000000000000000..ba55ed51c334db872d5b543500d572d231716cd1 GIT binary patch literal 51161 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 LILle%&_VzJfNn_~ literal 0 HcmV?d00001 diff --git a/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/drmemtrace.drmemtrace.signal_invariants.552324.5662.raw.gz b/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/drmemtrace.drmemtrace.signal_invariants.552324.5662.raw.gz new file mode 100644 index 0000000000000000000000000000000000000000..341870771eeae4b90d31476359170d73622a186a GIT binary patch literal 2498 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 M4OOZtmPjxF00vKKK>z>% literal 0 HcmV?d00001 diff --git a/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/funclist.log b/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/funclist.log new file mode 100644 index 00000000000..e69de29bb2d diff --git a/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/modules.log b/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw/raw/modules.log new file mode 100644 index 0000000000000000000000000000000000000000..d2343b81f6a65bcaa07c595a9157a89553556af9 GIT binary patch literal 12709 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 literal 0 HcmV?d00001 diff --git a/clients/drcachesim/tests/offline-legacy-int-offs.templatex b/clients/drcachesim/tests/offline-legacy-int-offs.templatex new file mode 100644 index 00000000000..3850f434f76 --- /dev/null +++ b/clients/drcachesim/tests/offline-legacy-int-offs.templatex @@ -0,0 +1,66 @@ +Basic counts tool results: +Total counts: + 109205 total \(fetched\) instructions + 6768 total unique \(fetched\) instructions + 93 total non-fetched instructions + 11 total prefetches + 23974 total data loads + 5544 total data stores + 0 total icache flushes + 0 total dcache flushes + 3 total threads + 136 total scheduling markers + 11 total transfer markers + 0 total function id markers + 0 total function return address markers + 0 total function argument markers + 0 total function return value markers + 9 total other markers +Thread 552306 counts: + 101049 \(fetched\) instructions + 6393 unique \(fetched\) instructions + 93 non-fetched instructions + 11 prefetches + 21712 data loads + 4442 data stores + 0 icache flushes + 0 dcache flushes + 110 scheduling markers + 11 transfer markers + 0 function id markers + 0 function return address markers + 0 function argument markers + 0 function return value markers + 3 other markers +Thread 552323 counts: + 4674 \(fetched\) instructions + 1028 unique \(fetched\) instructions + 0 non-fetched instructions + 0 prefetches + 1317 data loads + 651 data stores + 0 icache flushes + 0 dcache flushes + 14 scheduling markers + 0 transfer markers + 0 function id markers + 0 function return address markers + 0 function argument markers + 0 function return value markers + 3 other markers +Thread 552324 counts: + 3482 \(fetched\) instructions + 1008 unique \(fetched\) instructions + 0 non-fetched instructions + 0 prefetches + 945 data loads + 451 data stores + 0 icache flushes + 0 dcache flushes + 12 scheduling markers + 0 transfer markers + 0 function id markers + 0 function return address markers + 0 function argument markers + 0 function return value markers + 3 other markers diff --git a/clients/drcachesim/tests/trace_invariants.cpp b/clients/drcachesim/tests/trace_invariants.cpp index e1974624e00..d5e519319f8 100644 --- a/clients/drcachesim/tests/trace_invariants.cpp +++ b/clients/drcachesim/tests/trace_invariants.cpp @@ -84,6 +84,18 @@ trace_invariants_t::process_memref(const memref_t &memref) assert(memrefs_until_interrupt_[memref.data.tid] != 0); --memrefs_until_interrupt_[memref.data.tid]; } + if (memref.marker.type == TRACE_TYPE_MARKER && + prev_entry_[memref.data.tid].marker.type == TRACE_TYPE_MARKER && + prev_entry_[memref.data.tid].marker.marker_type == TRACE_MARKER_TYPE_RSEQ_ABORT) { + // The rseq marker must be immediately prior to the kernel event marker. + assert(memref.marker.marker_type == TRACE_MARKER_TYPE_KERNEL_EVENT); + } + if (memref.marker.type == TRACE_TYPE_MARKER && + memref.marker.marker_type == TRACE_MARKER_TYPE_RSEQ_ABORT) { + // Check that the rseq final instruction was not executed: that raw2trace + // rolled it back. + assert(memref.marker.marker_value != prev_instr_[memref.data.tid].instr.addr); + } // Check that the signal delivery marker is immediately followed by the // app's signal handler, which we have annotated with "prefetcht0 [1]". if (memref.data.type == TRACE_TYPE_PREFETCHT0 && memref.data.addr == 1) { @@ -189,23 +201,28 @@ trace_invariants_t::process_memref(const memref_t &memref) // Ensure signal handlers return to the interruption point. if (prev_xfer_marker_[memref.data.tid].marker.marker_type == TRACE_MARKER_TYPE_KERNEL_XFER) { - assert(memref.instr.addr == - pre_signal_instr_[memref.data.tid].top().instr.addr || - // Asynch will go to the subsequent instr. - memref.instr.addr == - pre_signal_instr_[memref.data.tid].top().instr.addr + - pre_signal_instr_[memref.data.tid].top().instr.size || + assert((memref.instr.addr == prev_xfer_int_pc_[memref.data.tid].top() && + (memref.instr.addr == + pre_signal_instr_[memref.data.tid].top().instr.addr || + // Asynch will go to the subsequent instr. + memref.instr.addr == + pre_signal_instr_[memref.data.tid].top().instr.addr + + pre_signal_instr_[memref.data.tid].top().instr.size || + // Too hard to figure out branch targets. We have the + // prev_xfer_int_pc_ though. + type_is_instr_branch( + pre_signal_instr_[memref.data.tid].top().instr.type) || + pre_signal_instr_[memref.data.tid].top().instr.type == + TRACE_TYPE_INSTR_SYSENTER)) || // Nested signal. XXX: This only works for our annotated test // signal_invariants. memref.instr.addr == app_handler_pc_ || // Marker for rseq abort handler. Not as unique as a prefetch, but // we need an instruction and not a data type. - memref.instr.type == TRACE_TYPE_INSTR_DIRECT_JUMP || - // Too hard to figure out branch targets. - type_is_instr_branch( - pre_signal_instr_[memref.data.tid].top().instr.type) || - pre_signal_instr_[memref.data.tid].top().instr.type == - TRACE_TYPE_INSTR_SYSENTER); + memref.instr.type == TRACE_TYPE_INSTR_DIRECT_JUMP); + // We assume paired signal entry-exit (so no longjmp and no rseq + // inside signal handlers). + prev_xfer_int_pc_[memref.data.tid].pop(); pre_signal_instr_[memref.data.tid].pop(); } #endif @@ -226,10 +243,13 @@ trace_invariants_t::process_memref(const memref_t &memref) memref.marker.marker_type == TRACE_MARKER_TYPE_KERNEL_XFER)) { if (knob_verbose_ >= 3) { std::cerr << "::" << memref.data.pid << ":" << memref.data.tid << ":: " - << "marker type " << memref.marker.marker_type << " value " - << memref.marker.marker_value << "\n"; + << "marker type " << memref.marker.marker_type << " value 0x" + << std::hex << memref.marker.marker_value << std::dec << "\n"; } #ifdef UNIX + if (memref.marker.marker_type == TRACE_MARKER_TYPE_KERNEL_EVENT) + prev_xfer_int_pc_[memref.data.tid].push(memref.marker.marker_value); + assert(memref.marker.marker_value != 0); if (memref.marker.marker_type == TRACE_MARKER_TYPE_KERNEL_EVENT && // Give up on back-to-back signals. prev_xfer_marker_[memref.data.tid].marker.marker_type != diff --git a/clients/drcachesim/tests/trace_invariants.h b/clients/drcachesim/tests/trace_invariants.h index 93b286860e0..fe562b45bee 100644 --- a/clients/drcachesim/tests/trace_invariants.h +++ b/clients/drcachesim/tests/trace_invariants.h @@ -59,6 +59,8 @@ class trace_invariants_t : public analysis_tool_t { std::unordered_map prev_instr_; std::unordered_map prev_xfer_marker_; #ifdef UNIX + // We only support sigreturn-using handlers so we have pairing: no longjmp. + std::unordered_map> prev_xfer_int_pc_; std::unordered_map prev_entry_; std::unordered_map prev_prev_entry_; std::unordered_map> pre_signal_instr_; diff --git a/clients/drcachesim/tools/view.cpp b/clients/drcachesim/tools/view.cpp index 83a1ac31a1b..a4ee80205b9 100644 --- a/clients/drcachesim/tools/view.cpp +++ b/clients/drcachesim/tools/view.cpp @@ -162,10 +162,28 @@ view_t::process_memref(const memref_t &memref) << memref.marker.marker_value << ">\n"; break; case TRACE_MARKER_TYPE_KERNEL_EVENT: - std::cerr << "\n"; + if (trace_version_ <= TRACE_ENTRY_VERSION_NO_KERNEL_PC) { + // Legacy traces just have the module offset. + std::cerr << "\n"; + } else { + std::cerr << "\n"; + } + break; + case TRACE_MARKER_TYPE_RSEQ_ABORT: + std::cerr << "\n"; break; case TRACE_MARKER_TYPE_KERNEL_XFER: - std::cerr << "\n"; + if (trace_version_ <= TRACE_ENTRY_VERSION_NO_KERNEL_PC) { + // Legacy traces just have the module offset. + std::cerr << "\n"; + } else { + std::cerr << "\n"; + } break; case TRACE_MARKER_TYPE_INSTRUCTION_COUNT: std::cerr << "version < OFFLINE_FILE_VERSION_KERNEL_INT_PC + ? TRACE_ENTRY_VERSION_NO_KERNEL_PC + : TRACE_ENTRY_VERSION; trace_entry_t entry; entry.type = TRACE_TYPE_HEADER; entry.size = 0; @@ -925,6 +927,7 @@ raw2trace_t::get_next_entry(void *tls) // be i/o bound (or ISA decode bound) and aren't worried about some extra copies // from the vector. auto tdata = reinterpret_cast(tls); + tdata->last_entry_is_split = false; if (!tdata->pre_read.empty()) { tdata->last_entry = tdata->pre_read[0]; tdata->pre_read.erase(tdata->pre_read.begin(), tdata->pre_read.begin() + 1); @@ -940,10 +943,31 @@ raw2trace_t::get_next_entry(void *tls) return &tdata->last_entry; } +const offline_entry_t * +raw2trace_t::get_next_entry_keep_prior(void *tls) +{ + auto tdata = reinterpret_cast(tls); + if (tdata->last_entry_is_split) { + // Cannot record two live split entries. + return nullptr; + } + VPRINT(4, "Remembering split entry for unreading both at once\n"); + tdata->last_split_first_entry = tdata->last_entry; + const offline_entry_t *next = get_next_entry(tls); + // Set this *after* calling get_next_entry as it clears the field. + tdata->last_entry_is_split = true; + return next; +} + void raw2trace_t::unread_last_entry(void *tls) { auto tdata = reinterpret_cast(tls); + if (tdata->last_entry_is_split) { + VPRINT(4, "Unreading both parts of split entry at once\n"); + tdata->pre_read.push_back(tdata->last_split_first_entry); + tdata->last_entry_is_split = false; + } tdata->pre_read.push_back(tdata->last_entry); } diff --git a/clients/drcachesim/tracer/raw2trace.h b/clients/drcachesim/tracer/raw2trace.h index 601abf322d8..9246829cb08 100644 --- a/clients/drcachesim/tracer/raw2trace.h +++ b/clients/drcachesim/tracer/raw2trace.h @@ -543,6 +543,13 @@ struct trace_header_t { * of the data, and #trace_converter_t will not attempt to dereference past the provided * pointer. * + *
  • const offline_entry_t *get_next_entry_keep_prior(void *tls) + * + * Records the currently stored last entry in order to remember two entries at once + * (for handling split two-entry markers) and then reads and returns a pointer to the + * next entry. A subsequent call to unread_last_entry() will put back both entries. + * Returns an emptry string on success or an error description on an error. + * *
  • void unread_last_entry(void *tls) * * Ensure that the next call to get_next_entry() re-reads the last value.
  • @@ -977,16 +984,17 @@ template class trace_converter_t { std::string process_memref(void *tls, trace_entry_t **buf_in, const instr_summary_t *instr, instr_summary_t::memref_summary_t memref, bool write, - std::unordered_map ®_vals, uint64_t cur_modoffs, - bool instrs_are_separate, OUT bool *reached_end_of_memrefs, - OUT bool *interrupted) + std::unordered_map ®_vals, uint64_t cur_pc, + uint64_t cur_offs, bool instrs_are_separate, + OUT bool *reached_end_of_memrefs, OUT bool *interrupted) { std::string error = append_memref(tls, buf_in, instr, memref, write, reg_vals, reached_end_of_memrefs); if (!error.empty()) return error; - error = handle_kernel_interrupt_and_markers( - tls, buf_in, cur_modoffs, instr->length(), instrs_are_separate, interrupted); + error = handle_kernel_interrupt_and_markers(tls, buf_in, cur_pc, cur_offs, + instr->length(), instrs_are_separate, + interrupted); return error; } @@ -1021,7 +1029,11 @@ template class trace_converter_t { TESTANY(OFFLINE_FILE_TYPE_FILTERED, impl()->get_file_type(tls)); bool is_instr_only_trace = TESTANY(OFFLINE_FILE_TYPE_INSTRUCTION_ONLY, impl()->get_file_type(tls)); - uint64_t cur_modoffs = in_entry->pc.modoffs; + uint64_t cur_pc = + reinterpret_cast(modvec_()[in_entry->pc.modidx].orig_seg_base) + + (in_entry->pc.modoffs - modvec_()[in_entry->pc.modidx].seg_offs); + // Legacy traces need the offset, not the pc. + uint64_t cur_offs = in_entry->pc.modoffs; std::unordered_map reg_vals; if (instr_count == 0) { // L0 filtering adds a PC entry with a count of 0 prior to each memref. @@ -1090,13 +1102,12 @@ template class trace_converter_t { // include a faulting instruction before its raised signal. bool interrupted = false; error = handle_kernel_interrupt_and_markers( - tls, &buf, cur_modoffs, instr->length(), instrs_are_separate, + tls, &buf, cur_pc, cur_offs, instr->length(), instrs_are_separate, &interrupted); if (!error.empty()) return error; if (interrupted) { - impl()->log(3, "Stopping bb at kernel interruption point +" PIFX "\n", - cur_modoffs); + impl()->log(3, "Stopping bb at kernel interruption point %p\n", cur_pc); } // We need to interleave instrs with memrefs. // There is no following memref for (instrs_are_separate && !skip_icache). @@ -1126,7 +1137,7 @@ template class trace_converter_t { // only the original app instr though. So we use the 0th // dest/src of the original scatter/gather instr for all. is_scatter ? instr->mem_dest_at(0) : instr->mem_src_at(0), - is_scatter, reg_vals, cur_modoffs, instrs_are_separate, + is_scatter, reg_vals, cur_pc, cur_offs, instrs_are_separate, &reached_end_of_memrefs, &interrupted); if (!error.empty()) return error; @@ -1137,7 +1148,7 @@ template class trace_converter_t { for (uint j = 0; j < instr->num_mem_srcs(); j++) { error = process_memref( tls, &buf, instr, instr->mem_src_at(j), false, reg_vals, - cur_modoffs, instrs_are_separate, nullptr, &interrupted); + cur_pc, cur_offs, instrs_are_separate, nullptr, &interrupted); if (!error.empty()) return error; if (interrupted) @@ -1148,7 +1159,7 @@ template class trace_converter_t { for (uint j = 0; !interrupted && j < instr->num_mem_dests(); j++) { error = process_memref( tls, &buf, instr, instr->mem_dest_at(j), true, reg_vals, - cur_modoffs, instrs_are_separate, nullptr, &interrupted); + cur_pc, cur_offs, instrs_are_separate, nullptr, &interrupted); if (!error.empty()) return error; if (interrupted) @@ -1156,7 +1167,8 @@ template class trace_converter_t { } } } - cur_modoffs += instr->length(); + cur_pc += instr->length(); + cur_offs += instr->length(); DR_CHECK((size_t)(buf - buf_start) < WRITE_BUFFER_SIZE, "Too many entries"); if (instr->is_cti()) { // In case this is the last branch prior to a thread switch, buffer it. We @@ -1181,14 +1193,15 @@ template class trace_converter_t { return ""; } - // Returns true if a kernel interrupt happened at cur_modoffs. + // Returns true if a kernel interrupt happened at cur_pc. // Outputs a kernel interrupt if this is the right location. // Outputs any other markers observed if !instrs_are_separate, since they // are part of this block and need to be inserted now. std::string handle_kernel_interrupt_and_markers(void *tls, INOUT trace_entry_t **buf_in, - uint64_t cur_modoffs, int instr_length, - bool instrs_are_separate, OUT bool *interrupted) + uint64_t cur_pc, uint64_t cur_offs, + int instr_length, bool instrs_are_separate, + OUT bool *interrupted) { // To avoid having to backtrack later, we read ahead to ensure we insert // an interrupt at the right place between memrefs or between instructions. @@ -1202,32 +1215,57 @@ template class trace_converter_t { append = false; if (in_entry->extended.type != OFFLINE_TYPE_EXTENDED || in_entry->extended.ext != OFFLINE_EXT_TYPE_MARKER) { - // Not a marker: just put it back below. - } else if (in_entry->extended.valueB == TRACE_MARKER_TYPE_KERNEL_EVENT) { + // Not a marker: just put it back. + impl()->unread_last_entry(tls); + continue; + } + // The kernel markers can take two entries, so we have to read both + // if present to get to the type. There is support for unreading + // both. + uintptr_t marker_val = 0; + std::string err = get_marker_value(tls, &in_entry, &marker_val); + if (!err.empty()) + return err; + if (in_entry->extended.valueB == TRACE_MARKER_TYPE_KERNEL_EVENT || + in_entry->extended.valueB == TRACE_MARKER_TYPE_RSEQ_ABORT) { // A signal/exception marker in the next entry could be at any point // among non-memref instrs, or it could be after this bb. - // We check the stored offset. - uint64_t int_modoffs = (uint64_t)in_entry->extended.valueA; - impl()->log(4, - "Checking whether reached signal/exception +" PIFX - " vs cur +" PIFX "\n", - int_modoffs, cur_modoffs); - if (int_modoffs == 0 || int_modoffs == cur_modoffs) { - impl()->log(4, "Signal/exception interrupted the bb @ +" PIFX "\n", - int_modoffs); + // We check the stored PC. + int version = impl()->get_version(tls); + bool at_interrupted_pc = false; + bool rseq_rollback = false; + if (version < OFFLINE_FILE_VERSION_KERNEL_INT_PC) { + // We have only the offs, so we can't handle differing modules for + // the source and target for legacy traces. + if (marker_val == cur_offs) + at_interrupted_pc = true; + } else { + if (marker_val == cur_pc) + at_interrupted_pc = true; + } + if (in_entry->extended.valueB == TRACE_MARKER_TYPE_RSEQ_ABORT || + (version < OFFLINE_FILE_VERSION_KERNEL_INT_PC && marker_val == 0)) { + // For the older version, we will not get here for Windows + // callbacks, the other event with a 0 modoffs, because they are + // always between bbs. (Unfortunately there's no simple way to + // assert or check that here or in the tracer.) + rseq_rollback = true; + } + impl()->log(4, "Checking whether reached signal/exception %p vs cur %p\n", + marker_val, cur_pc); + if (marker_val == 0 || at_interrupted_pc || rseq_rollback) { + impl()->log(4, "Signal/exception interrupted the bb @ %p\n", cur_pc); append = true; *interrupted = true; - if (int_modoffs == 0) { + if (rseq_rollback) { // This happens on rseq native aborts, where the trace instru // includes the rseq committing store before the native rseq // execution hits the native abort. Pretend the native abort // happened *before* the committing store by walking the store - // backward. We will not get here for Windows callbacks, the - // other event with a 0 modoffs, because they are always between - // bbs. (Unfortunately there's no simple way to assert or check - // that here or in the tracer.) + // backward. trace_type_t skipped_type; do { + impl()->log(4, "Rolling back entry for rseq abort\n"); --*buf_in; skipped_type = static_cast((*buf_in)->type); DR_ASSERT(*buf_in >= buf_start); @@ -1235,7 +1273,7 @@ template class trace_converter_t { skipped_type != TRACE_TYPE_INSTR_NO_FETCH); } } else { - // Put it back. We do not have a problem with other markers + // Put it back (below). We do not have a problem with other markers // following this, because we will have to hit the correct point // for this interrupt marker before we hit a memref entry, avoiding // the danger of wanting a memref entry, seeing a marker, continuing, @@ -1252,10 +1290,6 @@ template class trace_converter_t { append = !instrs_are_separate; } if (append) { - uintptr_t marker_val = 0; - std::string err = get_marker_value(tls, &in_entry, &marker_val); - if (!err.empty()) - return err; byte *buf = reinterpret_cast(*buf_in); buf += trace_metadata_writer_t::write_marker( buf, (trace_marker_type_t)in_entry->extended.valueB, marker_val); @@ -1285,7 +1319,8 @@ template class trace_converter_t { uintptr_t marker_val = static_cast((*entry)->extended.valueA); if ((*entry)->extended.valueB == TRACE_MARKER_TYPE_SPLIT_VALUE) { #ifdef X64 - const offline_entry_t *next = impl()->get_next_entry(tls); + // Keep the prior so we can unread both at once if we roll back. + const offline_entry_t *next = impl()->get_next_entry_keep_prior(tls); if (next == nullptr || next->extended.ext != OFFLINE_EXT_TYPE_MARKER) return "SPLIT_VALUE marker is not adjacent to 2nd entry"; marker_val = @@ -1295,6 +1330,21 @@ template class trace_converter_t { return "TRACE_MARKER_TYPE_SPLIT_VALUE unexpected for 32-bit"; #endif } + if ((*entry)->extended.valueB == TRACE_MARKER_TYPE_KERNEL_EVENT || + (*entry)->extended.valueB == TRACE_MARKER_TYPE_RSEQ_ABORT || + (*entry)->extended.valueB == TRACE_MARKER_TYPE_KERNEL_XFER) { + if (impl()->get_version(tls) >= OFFLINE_FILE_VERSION_KERNEL_INT_PC) { + // We convert the idx:offs to an absolute PC. + kernel_interrupted_raw_pc_t raw_pc; + raw_pc.combined_value = marker_val; + app_pc pc = modvec_()[raw_pc.pc.modidx].orig_seg_base + + (raw_pc.pc.modoffs - modvec_()[raw_pc.pc.modidx].seg_offs); + impl()->log(3, "Kernel marker: converting %p idx=%d with base=%p to %p\n", + marker_val, raw_pc.pc.modidx, + modvec_()[raw_pc.pc.modidx].orig_seg_base, pc); + marker_val = (uintptr_t)pc; + } // Else we've already marked as TRACE_ENTRY_VERSION_NO_KERNEL_PC. + } *value = marker_val; return ""; } @@ -1531,6 +1581,8 @@ class raw2trace_t : public trace_converter_t { // Overridable parts of the interface expected by trace_converter_t. virtual const offline_entry_t * get_next_entry(void *tls); + virtual const offline_entry_t * + get_next_entry_keep_prior(void *tls); virtual void unread_last_entry(void *tls); virtual std::string @@ -1563,6 +1615,7 @@ class raw2trace_t : public trace_converter_t { , version(0) , file_type(OFFLINE_FILE_TYPE_DEFAULT) , saw_header(false) + , last_entry_is_split(false) , prev_instr_was_rep_string(false) , last_decode_block_start(nullptr) , last_block_summary(nullptr) @@ -1586,6 +1639,9 @@ class raw2trace_t : public trace_converter_t { // Current trace conversion state. bool saw_header; offline_entry_t last_entry; + // For 2-entry markers we need a 2nd current-entry struct we can unread. + bool last_entry_is_split; + offline_entry_t last_split_first_entry; std::array out_buf; bool prev_instr_was_rep_string; app_pc last_decode_block_start; diff --git a/clients/drcachesim/tracer/tracer.cpp b/clients/drcachesim/tracer/tracer.cpp index 741c329a233..c3ffc553196 100644 --- a/clients/drcachesim/tracer/tracer.cpp +++ b/clients/drcachesim/tracer/tracer.cpp @@ -1379,19 +1379,46 @@ event_kernel_xfer(void *drcontext, const dr_kernel_xfer_info_t *info) default: DR_ASSERT(false && "unknown kernel xfer type"); return; } NOTIFY(2, "%s: type %d, sig %d\n", __FUNCTION__, info->type, info->sig); - /* TODO i3937: We need something similar to this for online too, to place signals - * inside instr bundles. + /* TODO i#3937: We need something similar to what raw2trace does with this info + * for online too, to place signals inside instr bundles. */ - if (op_offline.get_value() && info->source_mcontext != nullptr) { - /* Enable post-processing to figure out the ordering of this xfer vs - * non-memref instrs in the bb. - */ - uint64_t modoffs = reinterpret_cast(instru)->get_modoffs( - drcontext, info->source_mcontext->pc); - marker_val = static_cast(modoffs); + /* XXX i#4041: For rseq abort, offline post-processing rolls back the committing + * store so the abort happens at a reasonable point. We don't have a solution + * for online though. + */ + if (info->source_mcontext != nullptr) { + if (op_offline.get_value()) { + /* Enable post-processing to figure out the ordering of this xfer vs + * non-memref instrs in the bb. + * We'll turn this into an absolute PC in the final trace for offline traces + * to provide the interrupted PC, primarily for a kernel event arriving right + * after a branch to give a core simulator the branch target. + */ + uint modidx; + /* Just like PC entries, this is the offset from the base, not from + * the indexed segment. + */ + uint64_t modoffs = reinterpret_cast(instru)->get_modoffs( + drcontext, info->source_mcontext->pc, &modidx); + /* We save space by using the modidx,modoffs format instead of a raw PC. + * These 49 bits will always fit into the 48-bit value field unless the + * module index is very large, when it will take two entries, while using + * an absolute PC here might always take two entries for some modules. + */ + kernel_interrupted_raw_pc_t raw_pc = { 0 }; + raw_pc.pc.modidx = modidx; + raw_pc.pc.modoffs = modoffs; + marker_val = raw_pc.combined_value; + } else { + marker_val = reinterpret_cast(info->source_mcontext->pc); + } NOTIFY(3, "%s: source pc " PFX " => modoffs " PIFX "\n", __FUNCTION__, info->source_mcontext->pc, marker_val); } + if (info->type == DR_XFER_RSEQ_ABORT) { + BUF_PTR(data->seg_base) += instru->append_marker( + BUF_PTR(data->seg_base), TRACE_MARKER_TYPE_RSEQ_ABORT, marker_val); + } BUF_PTR(data->seg_base) += instru->append_marker(BUF_PTR(data->seg_base), marker_type, marker_val); if (file_ops_func.handoff_buf == NULL) diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index f9b55c6b970..5d3a652e0da 100644 --- a/suite/tests/CMakeLists.txt +++ b/suite/tests/CMakeLists.txt @@ -3788,6 +3788,22 @@ if (BUILD_CLIENTS) OFF) set(tool.drcacheoff.altbindir_basedir "${PROJECT_SOURCE_DIR}/clients/drcachesim/tests") + + # Test the legacy trace versions OFFLINE_FILE_VERSION_KERNEL_INT_PC-1 and + # TRACE_ENTRY_VERSION_NO_KERNEL_PC. This requires a checked-in trace and thus + # -alt_module_dir; it has gzipped .raw files; hence here next to the altbindir test. + set(srcdir + ${PROJECT_SOURCE_DIR}/clients/drcachesim/tests/drmemtrace.legacy-int-offs.raw) + set(locdir ${PROJECT_BINARY_DIR}/drmemtrace.legacy-int-offs) + file(REMOVE_RECURSE ${locdir}) + file(MAKE_DIRECTORY ${locdir}) + file(COPY ${srcdir}/raw DESTINATION ${locdir}/) + torunonly_api(tool.drcacheoff.legacy-int-offs "${drcachesim_path}" + "offline-legacy-int-offs.c" "" + "-indir;${locdir};-simulator_type;basic_counts;-alt_module_dir;${srcdir};-module_file;${locdir}/raw/modules.log" + OFF) + set(tool.drcacheoff.legacy-int-offs_basedir + "${PROJECT_SOURCE_DIR}/clients/drcachesim/tests") endif () ###########################################################################