-
Notifications
You must be signed in to change notification settings - Fork 561
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
i#5505 PT tracing: Add burst PT test with interrupted futex
Adds test where one of the threads is waiting on a futex when detach occurs. Such futex PT traces have been observed to fail in libipt decode. We also do not want such PT traces because they do not include real app behavior. This test is to ensure we do not include such syscalls in the final trace. Issue: #5505
- Loading branch information
1 parent
e3fbf73
commit 14a8777
Showing
6 changed files
with
280 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
/* ********************************************************** | ||
* Copyright (c) 2016-2024 Google, Inc. All rights reserved. | ||
* **********************************************************/ | ||
|
||
/* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
* | ||
* * Redistributions of source code must retain the above copyright notice, | ||
* this list of conditions and the following disclaimer. | ||
* | ||
* * Redistributions in binary form must reproduce the above copyright notice, | ||
* this list of conditions and the following disclaimer in the documentation | ||
* and/or other materials provided with the distribution. | ||
* | ||
* * Neither the name of Google, Inc. nor the names of its contributors may be | ||
* used to endorse or promote products derived from this software without | ||
* specific prior written permission. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE | ||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||
* DAMAGE. | ||
*/ | ||
|
||
// This application links in drmemtrace_static and acquires a trace during | ||
// a "burst" of execution that includes some system call traces collected | ||
// using Intel-PT. | ||
|
||
// This is set globally in CMake for other tests so easier to undef here. | ||
#undef DR_REG_ENUM_COMPATIBILITY | ||
|
||
#include "analyzer.h" | ||
#include "tools/basic_counts.h" | ||
#include "dr_api.h" | ||
#include "drmemtrace/drmemtrace.h" | ||
#include "drmemtrace/raw2trace.h" | ||
#include "mock_reader.h" | ||
#include "raw2trace_directory.h" | ||
#include "scheduler.h" | ||
|
||
#include <cassert> | ||
#include <unistd.h> | ||
#include <fstream> | ||
#include <iostream> | ||
#include <linux/futex.h> | ||
#include <pthread.h> | ||
#include <string> | ||
#include <sys/syscall.h> | ||
|
||
namespace dynamorio { | ||
namespace drmemtrace { | ||
|
||
#define FATAL_ERROR(msg, ...) \ | ||
do { \ | ||
fprintf(stderr, "ERROR: " msg "\n", ##__VA_ARGS__); \ | ||
fflush(stderr); \ | ||
exit(1); \ | ||
} while (0) | ||
|
||
/* The futex the child waits at initially. */ | ||
static uint32_t futex_var = 0xf00d; | ||
/* The futex the child is transferred to. */ | ||
static uint32_t futex_var_other = 0x8bad; | ||
|
||
static void * | ||
child_futex_wait(void *) | ||
{ | ||
long res = syscall(SYS_futex, &futex_var, FUTEX_WAIT, /*#val=*/0xf00d, | ||
/*timeout=*/nullptr, /*uaddr2=*/nullptr, /*val3=*/0); | ||
assert(res == 0); | ||
std::cerr << "Child released from futex\n"; | ||
return NULL; | ||
} | ||
|
||
static void | ||
parent_futex_wake() | ||
{ | ||
/* The child would be waiting at the other futex by now. | ||
* iX: Note that the child thread undergoes detach while it is waiting | ||
* on futex_var_other. There is a bug at this point due to a possible | ||
* transparency violation. When the child thread restarts futex after | ||
* being interrupted by the detach signal, it seems like it resumes | ||
* waiting at the original futex_var instead of futex_var_other. | ||
* If we modify this code to do detach after this call, then the child | ||
* is found to be waiting at futex_var_other, as expected. | ||
*/ | ||
uint32_t *child_waiting_at_futex = &futex_var; | ||
long res = syscall(SYS_futex, child_waiting_at_futex, FUTEX_WAKE, /*#wakeup=*/1, | ||
/*timeout=*/nullptr, /*uaddr2=*/nullptr, /*val3=*/0); | ||
assert(res == 1); | ||
} | ||
|
||
static void | ||
parent_futex_reque() | ||
{ | ||
long res; | ||
do { | ||
/* Repeat until the child is surely waiting at the futex. We'll know this | ||
* when the following call returns a 1, which means the child was | ||
* transferred to the other futex var. | ||
*/ | ||
res = syscall(SYS_futex, &futex_var, FUTEX_CMP_REQUEUE, /*#wakeup_max=*/0, | ||
/*#requeue_max=*/1, /*uaddr2=*/&futex_var_other, /*val3=*/0xf00d); | ||
assert(res == 0 || res == 1); | ||
} while (res == 0); | ||
} | ||
|
||
static int | ||
do_some_syscalls() | ||
{ | ||
getpid(); | ||
gettid(); | ||
return 1; | ||
} | ||
|
||
static std::string | ||
postprocess(void *dr_context) | ||
{ | ||
std::cerr << "Post-processing the trace\n"; | ||
// Get path to write the final trace to. | ||
const char *raw_dir; | ||
drmemtrace_status_t mem_res = drmemtrace_get_output_path(&raw_dir); | ||
assert(mem_res == DRMEMTRACE_SUCCESS); | ||
std::string outdir = std::string(raw_dir) + DIRSEP + "post_processed"; | ||
|
||
const char *kcore_path; | ||
drmemtrace_status_t kcore_res = drmemtrace_get_kcore_path(&kcore_path); | ||
assert(kcore_res == DRMEMTRACE_SUCCESS); | ||
|
||
raw2trace_directory_t dir; | ||
if (!dr_create_dir(outdir.c_str())) | ||
FATAL_ERROR("Failed to create output dir."); | ||
std::string dir_err = dir.initialize(raw_dir, outdir, DEFAULT_TRACE_COMPRESSION_TYPE, | ||
/*syscall_template_file=*/""); | ||
assert(dir_err.empty()); | ||
raw2trace_t raw2trace(dir.modfile_bytes_, dir.in_files_, dir.out_files_, | ||
dir.out_archives_, dir.encoding_file_, | ||
dir.serial_schedule_file_, dir.cpu_schedule_file_, dr_context, | ||
/*verbosity=*/0, /*worker_count=*/-1, | ||
/*alt_module_dir=*/"", | ||
/*chunk_instr_count=*/10 * 1000 * 1000, dir.in_kfiles_map_, | ||
dir.kcoredir_, /*kallsyms_path=*/"", | ||
/*syscall_template_file=*/nullptr, | ||
// We want to fail if any error is encountered. | ||
/*pt2ir_best_effort=*/false); | ||
std::string error = raw2trace.do_conversion(); | ||
if (!error.empty()) | ||
FATAL_ERROR("raw2trace failed: %s\n", error.c_str()); | ||
uint64 decoded_syscall_count = | ||
raw2trace.get_statistic(RAW2TRACE_STAT_SYSCALL_TRACES_CONVERTED); | ||
if (decoded_syscall_count <= 2) { | ||
std::cerr << "Incorrect decoded syscall count (found: " << decoded_syscall_count | ||
<< " vs expected > 2)\n"; | ||
} | ||
return outdir; | ||
} | ||
|
||
static basic_counts_t::counters_t | ||
get_basic_counts(const std::string &trace_dir) | ||
{ | ||
auto basic_counts_tool = | ||
std::unique_ptr<basic_counts_t>(new basic_counts_t(/*verbose=*/0)); | ||
std::vector<analysis_tool_t *> tools; | ||
tools.push_back(basic_counts_tool.get()); | ||
analyzer_t analyzer(trace_dir, &tools[0], static_cast<int>(tools.size())); | ||
if (!analyzer) { | ||
FATAL_ERROR("failed to initialize analyzer: %s", | ||
analyzer.get_error_string().c_str()); | ||
} | ||
if (!analyzer.run()) { | ||
FATAL_ERROR("failed to run analyzer: %s", analyzer.get_error_string().c_str()); | ||
} | ||
return basic_counts_tool->get_total_counts(); | ||
} | ||
|
||
static void | ||
gather_trace() | ||
{ | ||
if (setenv("DYNAMORIO_OPTIONS", | ||
"-stderr_mask 0xc -client_lib ';;-offline -enable_kernel_tracing", | ||
1 /*override*/) != 0) | ||
std::cerr << "failed to set env var!\n"; | ||
dr_app_setup(); | ||
assert(!dr_app_running_under_dynamorio()); | ||
dr_app_start(); | ||
|
||
pthread_t child_thread; | ||
int res = pthread_create(&child_thread, NULL, child_futex_wait, NULL); | ||
assert(res == 0); | ||
|
||
/* Ensure that the child is waiting at a futex. */ | ||
parent_futex_reque(); | ||
|
||
do_some_syscalls(); | ||
|
||
dr_app_stop_and_cleanup(); | ||
|
||
/* Wake up the child finally. */ | ||
parent_futex_wake(); | ||
|
||
pthread_join(child_thread, NULL); | ||
|
||
return; | ||
} | ||
|
||
static int | ||
test_pt_trace(void *dr_context) | ||
{ | ||
std::string trace_dir = postprocess(dr_context); | ||
basic_counts_t::counters_t final_trace_counts = get_basic_counts(trace_dir); | ||
if (final_trace_counts.kernel_instrs == 0) { | ||
std::cerr << "Unexpected kernel instr count in the final trace (" | ||
<< final_trace_counts.kernel_instrs << ")\n"; | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
|
||
int | ||
test_main(int argc, const char *argv[]) | ||
{ | ||
gather_trace(); | ||
void *dr_context = dr_standalone_init(); | ||
if (test_pt_trace(dr_context)) { | ||
return 1; | ||
} | ||
dr_standalone_exit(); | ||
return 0; | ||
} | ||
|
||
} // namespace drmemtrace | ||
} // namespace dynamorio |
8 changes: 8 additions & 0 deletions
8
clients/drcachesim/tests/offline-burst_syscall_pt_SUDO.template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
ERROR: PT tracing for the last syscall .* of thread .* was found active at detach. | ||
Child released from futex | ||
Post-processing the trace | ||
Syscall mix tool results: | ||
syscall count : syscall_num | ||
.* | ||
syscall trace count : syscall_num | ||
.* |
8 changes: 8 additions & 0 deletions
8
clients/drcachesim/tests/offline-burst_syscall_pt_SUDO.templatex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
ERROR: PT tracing for the last syscall .* of thread .* was found active at detach. | ||
Child released from futex | ||
Post-processing the trace | ||
Syscall mix tool results: | ||
syscall count : syscall_num | ||
.* | ||
syscall trace count : syscall_num | ||
.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters