diff --git a/clients/drcachesim/CMakeLists.txt b/clients/drcachesim/CMakeLists.txt index e23d5c07ed8dc710523493a87cc7dc75ed5033e8..d4d3af8d69fa91539cd4aab957811500b7246497 100644 --- a/clients/drcachesim/CMakeLists.txt +++ b/clients/drcachesim/CMakeLists.txt @@ -186,6 +186,12 @@ endif () if (liblz4) target_link_libraries(drmemtrace_raw2trace lz4) endif () +if (BUILD_PT_POST_PROCESSOR) + add_definitions(-DBUILD_PT_POST_PROCESSOR) + # Add the directory containing libipt.so and libipt-sb.so to the linker search path. + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='${PROJECT_BINARY_DIR}/lib'") + target_link_libraries(drmemtrace_raw2trace drpt2ir drir2trace) +endif (BUILD_PT_POST_PROCESSOR) set(drcachesim_srcs launcher.cpp @@ -337,6 +343,7 @@ macro(add_drmemtrace name type) if (BUILD_PT_TRACER) set(drmemtrace_srcs ${drmemtrace_srcs} tracer/syscall_pt_trace.cpp + tracer/kernel_image.cpp ) add_definitions(-DBUILD_PT_TRACER) endif () diff --git a/clients/drcachesim/analyzer_multi.cpp b/clients/drcachesim/analyzer_multi.cpp index 8968636c481f13ac609ab240551013157f852fd0..796cbe27687270af818a2a5327c4b09fe4bf615c 100644 --- a/clients/drcachesim/analyzer_multi.cpp +++ b/clients/drcachesim/analyzer_multi.cpp @@ -108,7 +108,8 @@ analyzer_multi_t::analyzer_multi_t() raw2trace_t raw2trace( dir.modfile_bytes_, dir.in_files_, dir.out_files_, dir.out_archives_, dir.encoding_file_, nullptr, op_verbose.get_value(), op_jobs.get_value(), - op_alt_module_dir.get_value(), op_chunk_instr_count.get_value()); + dir.get_syscall_pt_trace_dir(), op_alt_module_dir.get_value(), + op_chunk_instr_count.get_value()); std::string error = raw2trace.do_conversion(); if (!error.empty()) { success_ = false; diff --git a/clients/drcachesim/drpt2trace/CMakeLists.txt b/clients/drcachesim/drpt2trace/CMakeLists.txt index 687eacd48fd7ea63772f3e6547c430d978d45aa7..00af3207219e66d480b780f5b2fbcabdb5b0cb40 100644 --- a/clients/drcachesim/drpt2trace/CMakeLists.txt +++ b/clients/drcachesim/drpt2trace/CMakeLists.txt @@ -77,12 +77,14 @@ set(ipt-ext_srcs add_library(ipt-ext STATIC ${ipt-ext_srcs}) add_dependencies(ipt-ext ipt) target_link_libraries(ipt-ext ipt) +DR_export_target(ipt-ext) +install_exported_target(ipt-ext ${PROJECT_BINARY_DIR}/lib) # drpt2ir don't include configure.h so they don't get DR defines add_dr_defines() -# We build drpt2ir to a static library. So we can link it in the independent -# clients drpt2trace and drraw2trace. +# We build drpt2ir and drir2trace to static libraries. So we can link them to the +# independent clients drpt2trace and raw2trace. set(drpt2ir_srcs pt2ir.cpp ) @@ -91,17 +93,29 @@ configure_DynamoRIO_decoder(drpt2ir) add_dependencies(drpt2ir ipt ipt-sb ipt-ext api_headers) target_link_libraries(drpt2ir ipt ipt-sb ipt-ext) install_client_nonDR_header(drmemtrace pt2ir.h) +DR_export_target(drpt2ir) +install_exported_target(drpt2ir ${INSTALL_CLIENTS_LIB}) -# TODO i#5505: Currently, drpt2trace only counts instructions. -# In the future, we will add support for converting pt trace to memref_t. -# So we may need to link drdecode to drpt2ir library. +set(drir2trace_srcs + ir2trace.cpp +) +add_library(drir2trace STATIC ${drir2trace_srcs}) +configure_DynamoRIO_decoder(drir2trace) +add_dependencies(drir2trace api_headers) +install_client_nonDR_header(drmemtrace ir2trace.h) +DR_export_target(drir2trace) +install_exported_target(drir2trace ${INSTALL_CLIENTS_LIB}) + +# drpt2trace is a client that uses drpt2ir and drir2trace to convert PT data to memref_t. set(drpt2trace_srcs drpt2trace.cpp ) add_executable(drpt2trace ${drpt2trace_srcs}) configure_DynamoRIO_standalone(drpt2trace) use_DynamoRIO_extension(drpt2trace droption) -add_dependencies(drpt2trace drpt2ir) +add_dependencies(drpt2trace drpt2ir drir2trace) + # Add the directory containing libipt.so and libipt-sb.so to the linker search path. -SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='${PROJECT_BINARY_DIR}/lib'") -target_link_libraries(drpt2trace drpt2ir) +set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='${PROJECT_BINARY_DIR}/lib'") +target_link_libraries(drpt2trace drpt2ir drir2trace) diff --git a/clients/drcachesim/drpt2trace/drir.h b/clients/drcachesim/drpt2trace/drir.h new file mode 100644 index 0000000000000000000000000000000000000000..27f3f5384265f1e85e97714701a7c7bed9a30e21 --- /dev/null +++ b/clients/drcachesim/drpt2trace/drir.h @@ -0,0 +1,69 @@ +/* ********************************************************** + * Copyright (c) 2022 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 VMWARE, 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. + */ + +/* drir: the type wraps instrlist_t. */ + +#ifndef _DRIR_H_ +#define _DRIR_H_ 1 + +#define DR_FAST_IR 1 +#include "dr_api.h" +#include <iostream> + +/* The auto cleanup wrapper of instrlist_t. + * This can ensure the instance of instrlist_t is cleaned up when it is out of scope. + */ +struct instrlist_autoclean_t { +public: + instrlist_autoclean_t(void *drcontext, instrlist_t *data) + : drcontext(drcontext) + , data(data) + { + } + ~instrlist_autoclean_t() + { +#ifdef DEBUG + if (drcontext == nullptr) { + std::cerr << "instrlist_autoclean_t: invalid drcontext" << std::endl; + exit(1); + } +#endif + if (data != nullptr) { + instrlist_clear_and_destroy(drcontext, data); + data = nullptr; + } + } + void *drcontext = nullptr; + instrlist_t *data = nullptr; +}; + +#endif /* _DRIR_H_ */ diff --git a/clients/drcachesim/drpt2trace/drpt2trace.cpp b/clients/drcachesim/drpt2trace/drpt2trace.cpp index 982eb731b3a22b15a87a28f3a3e9a41b79bd7b00..b1f79336025b05517038258c7b3cd4f80bd33832 100644 --- a/clients/drcachesim/drpt2trace/drpt2trace.cpp +++ b/clients/drcachesim/drpt2trace/drpt2trace.cpp @@ -48,6 +48,7 @@ #include "droption.h" #include "pt2ir.h" +#include "ir2trace.h" #define CLIENT_NAME "drpt2trace" #define SUCCESS 0 @@ -67,6 +68,16 @@ static droption_t<bool> op_help(DROPTION_SCOPE_FRONTEND, "help", false, static droption_t<bool> op_print_trace(DROPTION_SCOPE_FRONTEND, "print_trace", false, "Print trace", "Print the disassemble code of the trace."); +static droption_t<std::string> op_mode( + DROPTION_SCOPE_FRONTEND, "mode", "", + "[Required] The mode for decoding the trace. Valid modes are: ELF, SIDEBAND, DR", + "Specifies the mode for decoding the trace. Valid modes are:\n" + "ELF: The raw bits of this PT trace are all in one elf file. \n" + "SIDEBAND: The raw bits of this PT trace is in different image files, and the " + "sideband data contains the image switching info that can be used in the decoding " + "process. \n" + "DR: The raw bits of this PT trace are in the kernel image file, and the metadata of " + "the code segments in kernel image file is in the kernel image metadata file.\n"); static droption_t<std::string> op_raw_pt(DROPTION_SCOPE_FRONTEND, "raw_pt", "", @@ -90,6 +101,16 @@ static droption_t<std::string> op_raw_pt_metadata( "[Optional] Path to the metadata file of PT raw trace", "Specifies the file path of the metadata file of PT raw trace. This file is " "generated by the client drcachesim."); +static droption_t<std::string> + op_kernel_image(DROPTION_SCOPE_FRONTEND, "kernel_image", "", + "[Optional] Path to the file of kernel image", + "Specifies the file path of the file of kernel image. This file is " + "generated by the client drcachesim."); +static droption_t<std::string> op_kernel_image_metadata( + DROPTION_SCOPE_FRONTEND, "kernel_image_metadata", "", + "[Optional] Path to the metadata file of kernel image", + "Specifies the file path of the metadata file of kernel image. This file is " + "generated by the client drcachesim."); static droption_t<std::string> op_primary_sb(DROPTION_SCOPE_FRONTEND, "primary_sb", "", @@ -213,25 +234,13 @@ static droption_t<unsigned long long> op_sb_kernel_start( */ static void -print_results(IN instrlist_autoclean_t &ilist) +print_results(IN instrlist_autoclean_t &drir, IN std::vector<trace_entry_t> &entries) { - if (ilist.data == nullptr) { - std::cerr << "The list to store decoded instructions is not initialized." - << std::endl; - return; - } - instr_t *instr = instrlist_first(ilist.data); - uint64_t count = 0; - while (instr != NULL) { - count++; - instr = instr_get_next(instr); - } - if (op_print_trace.specified()) { /* Print the disassemble code of the trace. */ - instrlist_disassemble(GLOBAL_DCONTEXT, 0, ilist.data, STDOUT); + instrlist_disassemble(GLOBAL_DCONTEXT, 0, drir.data, STDOUT); } - std::cout << "Number of Instructions: " << count << std::endl; + std::cout << "Number of Instructions: " << entries.size() << std::endl; } /**************************************************************************** @@ -264,6 +273,11 @@ option_init(int argc, const char *argv[]) print_usage(); return false; } + if (!op_mode.specified()) { + std::cerr << CLIENT_NAME << "Usage error: mode must be specified" << std::endl; + print_usage(); + return false; + } if (!op_raw_pt.specified()) { std::cerr << CLIENT_NAME << ": option " << op_raw_pt.get_name() << " is required." << std::endl; @@ -274,32 +288,52 @@ option_init(int argc, const char *argv[]) /* Because Intel PT doesn't save instruction bytes or memory contents, the converter * needs the instruction bytes for each IPs (Instruction Pointers) to decode the PT * trace. - * drpt2trace supports two modes to convert: - * (1) Sideband Mode: the user must provide sideband data and parameters. In this + * drpt2trace supports three modes to convert: + * (1) SIDEBAND Mode: the user must provide sideband data and parameters. In this * mode, the converter uses sideband decoders to simulate image switches during the * conversion. For example, we can use this mode to convert the traces where the * instruction bytes are located in multiple images. - * (2) Elf Mode: the user needs to provide an elf image for the PT trace. This mode is + * (2) ELF Mode: the user needs to provide an elf image for the PT trace. This mode is * for cases where the PT trace's instruction bytes belong to one image. So we can use * this mode to convert the kernel trace and the short-term user trace where it's * likely that we'll not have an image switch. + * (3) DR Mode: this mode is used to check whether the syscall PT trace generated by + * drcachesim is correct. */ - if ((!op_elf.specified() && !op_primary_sb.specified()) || - (op_elf.specified() && op_primary_sb.specified())) { - std::cerr << CLIENT_NAME - << ": exactly one of the options --elf, --primary_sb must be specified." + if (op_mode.get_value() == "ELF") { + /* Check if the required options for ELF mode are specified. */ + if (!op_elf.specified()) { + std::cerr << CLIENT_NAME << ": option " << op_elf.get_name() + << " is required in " << op_mode.get_value() << " mode." + << std::endl; + print_usage(); + return false; + } + } else if (op_mode.get_value() == "SIDEBAND") { + /* Check if the required options for SIDEBAND mode are specified. */ + if (!op_primary_sb.specified() || !op_sb_sample_type.specified()) { + std::cerr << CLIENT_NAME << ": option " << op_primary_sb.get_name() << " and " + << op_sb_sample_type.get_name() << " are required in " + << op_mode.get_value() << " mode." << std::endl; + print_usage(); + return false; + } + } else if (op_mode.get_value() == "DR") { + /* Check if the required options for DR mode are specified. */ + if (!op_kernel_image.specified() || !op_kernel_image_metadata.specified()) { + std::cerr << CLIENT_NAME << ": option " << op_kernel_image.get_name() + << " and " << op_kernel_image_metadata.get_name() + << " are required in " << op_mode.get_value() << " mode." + << std::endl; + print_usage(); + return false; + } + } else { + std::cerr << CLIENT_NAME << ": option " << op_mode.get_name() << " is invalid." << std::endl; print_usage(); return false; } - - /* Check if the required options for sideband mode are specified. */ - if (op_primary_sb.specified() && !op_sb_sample_type.specified()) { - std::cerr << CLIENT_NAME << ": option " << op_sb_sample_type.get_name() - << " is required in sideband mode." << std::endl; - print_usage(); - return false; - } return true; } @@ -329,6 +363,8 @@ main(int argc, const char *argv[]) config.raw_file_path = op_raw_pt.get_value(); config.elf_file_path = op_elf.get_value(); config.elf_base = op_elf_base.get_value(); + config.kernel_image_path = op_kernel_image.get_value(); + config.kernel_image_metadata_path = op_kernel_image_metadata.get_value(); config.sb_primary_file_path = op_primary_sb.get_value(); std::istringstream op_secondary_sb_stream(op_secondary_sb.get_value()); copy(std::istream_iterator<std::string>(op_secondary_sb_stream), @@ -362,16 +398,26 @@ main(int argc, const char *argv[]) std::cerr << CLIENT_NAME << ": failed to initialize pt2ir_t." << std::endl; return FAILURE; } - instrlist_autoclean_t ilist = { GLOBAL_DCONTEXT, nullptr }; - pt2ir_convert_status_t status = ptconverter->convert(ilist); - if (status != PT2IR_CONV_SUCCESS) { + instrlist_autoclean_t drir = { GLOBAL_DCONTEXT, nullptr }; + pt2ir_convert_status_t pt2ir_convert_status = ptconverter->convert(drir); + if (pt2ir_convert_status != PT2IR_CONV_SUCCESS) { std::cerr << CLIENT_NAME << ": failed to convert PT raw trace to DR IR." - << "[error status: " << status << "]" << std::endl; + << "[error status: " << pt2ir_convert_status << "]" << std::endl; + return FAILURE; + } + + /* Convert the DR IR to trace entries. */ + std::vector<trace_entry_t> entries; + ir2trace_convert_status_t ir2trace_convert_status = + ir2trace_t::convert(drir, entries); + if (ir2trace_convert_status != IR2TRACE_CONV_SUCCESS) { + std::cerr << CLIENT_NAME << ": failed to convert DR IR to trace entries" + << "[error status: " << ir2trace_convert_status << "]" << std::endl; return FAILURE; } /* Print the count and the disassemble code of DR IR. */ - print_results(ilist); + print_results(drir, entries); return SUCCESS; } diff --git a/clients/drcachesim/drpt2trace/ir2trace.cpp b/clients/drcachesim/drpt2trace/ir2trace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cf38048b51fb2ef4d14dd9ccd850ccdbbbf6a927 --- /dev/null +++ b/clients/drcachesim/drpt2trace/ir2trace.cpp @@ -0,0 +1,55 @@ +/* ********************************************************** + * Copyright (c) 2022 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 VMWARE, 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. + */ + +/* ir2trace: convert DynamoRIO's IR format to trace entries. */ + +#include "ir2trace.h" +#include "dr_api.h" + +ir2trace_convert_status_t +ir2trace_t::convert(IN instrlist_autoclean_t &ilist, + OUT std::vector<trace_entry_t> &trace) +{ + if (ilist.data == NULL) { + return IR2TRACE_CONV_ERROR_INVALID_PARAMETER; + } + instr_t *instr = instrlist_first(ilist.data); + while (instr != NULL) { + trace_entry_t entry = {}; + entry.type = TRACE_TYPE_INSTR; + entry.size = instr_length(GLOBAL_DCONTEXT, instr); + entry.addr = (uintptr_t)instr_get_app_pc(instr); + instr = instr_get_next(instr); + trace.push_back(entry); + } + return IR2TRACE_CONV_SUCCESS; +} diff --git a/clients/drcachesim/drpt2trace/ir2trace.h b/clients/drcachesim/drpt2trace/ir2trace.h new file mode 100644 index 0000000000000000000000000000000000000000..8434b83824fc1a237f82a9144d520afba32872b7 --- /dev/null +++ b/clients/drcachesim/drpt2trace/ir2trace.h @@ -0,0 +1,87 @@ +/* ********************************************************** + * Copyright (c) 2022 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 VMWARE, 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. + */ + +/* ir2trace: convert DynamoRIO's IR format to trace entries. */ + +#ifndef _IR2TRACE_H_ +#define _IR2TRACE_H_ 1 + +/** + * @file ir2trace.h + * @brief Offline DynamoRIO's IR converter. Converts DynamoRIO's IR format to trace + * entries. + */ + +#include <vector> +#include "drir.h" +#include "../common/trace_entry.h" + +#ifndef IN +# define IN // nothing +#endif +#ifndef OUT +# define OUT // nothing +#endif +#ifndef INOUT +# define INOUT // nothing +#endif + +/** + * The type of ir2trace_t::convert() return value. + */ +enum ir2trace_convert_status_t { + /** The conversion process succeeded. */ + IR2TRACE_CONV_SUCCESS = 0, + /** The conversion process failed: invalid parameter. */ + IR2TRACE_CONV_ERROR_INVALID_PARAMETER +}; + +class ir2trace_t { +public: + ir2trace_t() + { + } + ~ir2trace_t() + { + } + + /** + * Returns ir2trace_convert_status_t. If the convertion is successful, the function + * returns IR2TRACE_CONV_SUCCESS. Otherwise, the function returns the corresponding + * error code. + * \note The convert function convert the DR's IR format to trace entries. + */ + static ir2trace_convert_status_t + convert(IN instrlist_autoclean_t &ilist, OUT std::vector<trace_entry_t> &trace); +}; + +#endif /* _IR2TRACE_H_ */ diff --git a/clients/drcachesim/drpt2trace/pt2ir.cpp b/clients/drcachesim/drpt2trace/pt2ir.cpp index fd594d136e76679039bfe8b15f684ed1fa0dfbc1..59ad36286186c892165d6067bb6a9f362d0eeeee 100644 --- a/clients/drcachesim/drpt2trace/pt2ir.cpp +++ b/clients/drcachesim/drpt2trace/pt2ir.cpp @@ -34,7 +34,6 @@ #include <stdlib.h> #include <iostream> #include <string.h> -#include <inttypes.h> #include <errno.h> #include <fstream> @@ -44,6 +43,7 @@ extern "C" { #include "load_elf.h" } +#include "dr_api.h" #define ERRMSG_HEADER() \ do { \ @@ -157,7 +157,7 @@ pt2ir_t::init(IN pt2ir_config_t &pt2ir_config) /* Load kcore to sideband kernel image cache. */ if (!pt2ir_config.kcore_path.empty()) { - if (!load_kernel_image(pt2ir_config.kcore_path)) { + if (!load_kcore(pt2ir_config.kcore_path)) { ERRMSG("Failed to load kernel image: %s\n", pt2ir_config.kcore_path.c_str()); return false; } @@ -207,12 +207,24 @@ pt2ir_t::init(IN pt2ir_config_t &pt2ir_config) } } + if (!pt2ir_config.kernel_image_path.empty() && + !pt2ir_config.kernel_image_metadata_path.empty()) { + if (!load_kernel_image(pt2ir_config.kernel_image_path, + pt2ir_config.kernel_image_metadata_path)) { + ERRMSG("Failed to load kernel image: %s %s.\n", + pt2ir_config.kernel_image_path.c_str(), + pt2ir_config.kernel_image_metadata_path.c_str()); + return false; + } + } + return true; } pt2ir_convert_status_t pt2ir_t::convert(OUT instrlist_autoclean_t &ilist) { + bool append = false; /* Initializes an empty instruction list to store all DynamoRIO's IR list converted * from PT IR. */ @@ -336,9 +348,23 @@ pt2ir_t::convert(OUT instrlist_autoclean_t &ilist) dr_fprintf(STDOUT, ">\n"); #endif } - instrlist_append(ilist.data, instr); + // dr_printf("" PFX "\n", insn.ip); + if (append) { + instrlist_append(ilist.data, instr); + } + if (insn.ip == 0xffffffff8bc00191) { + append = !append; + if (append == false) { + return PT2IR_CONV_SUCCESS; + } + } } } + + if (append) { + instrlist_clear_and_destroy(GLOBAL_DCONTEXT, ilist.data); + ilist.data = nullptr; + } return PT2IR_CONV_SUCCESS; } @@ -378,7 +404,7 @@ pt2ir_t::load_pt_raw_file(IN std::string &path) } bool -pt2ir_t::load_kernel_image(IN std::string &path) +pt2ir_t::load_kcore(IN std::string &path) { /* Load all ELF sections in kcore to the shared image cache. * XXX: load_elf() is implemented in libipt's client ptxed. Currently we directly use @@ -394,6 +420,38 @@ pt2ir_t::load_kernel_image(IN std::string &path) return true; } +bool +pt2ir_t::load_kernel_image(IN std::string &path, IN std::string &metadata_path) +{ + std::ifstream metadata_f(metadata_path, std::ios::in); + if (!metadata_f.is_open()) { + ERRMSG("Failed to open metadata file: %s.\n", metadata_path.c_str()); + return false; + } + std::string line; + while (std::getline(metadata_f, line)) { + uint64_t offset = 0, len = 0, vaddr = 0; + if (sscanf(line.c_str(), + HEX64_FORMAT_STRING " " HEX64_FORMAT_STRING " " HEX64_FORMAT_STRING, + &offset, &len, &vaddr) < 3) { + ERRMSG("Failed to parse metadata file: %s(%s).\n", metadata_path.c_str(), + line.c_str()); + metadata_f.close(); + return false; + } + int errcode = pt_image_add_file(pt_insn_get_image(pt_instr_decoder_), + path.c_str(), offset, len, NULL, vaddr); + if (errcode < 0) { + ERRMSG("Failed to load kernel image %s: %s.\n", path.c_str(), + pt_errstr(pt_errcode(errcode))); + metadata_f.close(); + return false; + } + } + metadata_f.close(); + return true; +} + bool pt2ir_t::alloc_sb_pevent_decoder(IN struct pt_sb_pevent_config &config) { @@ -418,9 +476,10 @@ pt2ir_t::dx_decoding_error(IN int errcode, IN const char *errtype, IN uint64_t i err = pt_insn_get_offset(pt_instr_decoder_, &pos); if (err < 0) { ERRMSG("Could not determine offset: %s\n", pt_errstr(pt_errcode(err))); - ERRMSG("[?, %" PRIx64 "] %s: %s\n", ip, errtype, pt_errstr(pt_errcode(errcode))); - } else { - ERRMSG("[%" PRIx64 ", IP:%" PRIx64 "] %s: %s\n", pos, ip, errtype, + ERRMSG("[?, " HEX64_FORMAT_STRING "] %s: %s\n", ip, errtype, pt_errstr(pt_errcode(errcode))); + } else { + ERRMSG("[" HEX64_FORMAT_STRING ", IP:" HEX64_FORMAT_STRING "] %s: %s\n", pos, ip, + errtype, pt_errstr(pt_errcode(errcode))); } } diff --git a/clients/drcachesim/drpt2trace/pt2ir.h b/clients/drcachesim/drpt2trace/pt2ir.h index 00d8dd65447aaa6689ecf8aa75c3fdd0a35679b9..ebe74f23ae5f173187c9646125cf8aaba2ae83b7 100644 --- a/clients/drcachesim/drpt2trace/pt2ir.h +++ b/clients/drcachesim/drpt2trace/pt2ir.h @@ -45,8 +45,7 @@ #include <iostream> #include <fstream> #include <memory> -#define DR_FAST_IR 1 -#include "dr_api.h" +#include "drir.h" #ifndef IN # define IN // nothing @@ -58,33 +57,6 @@ # define INOUT // nothing #endif -/* The auto cleanup wrapper of instrlist_t. - * This can ensure the instance of instrlist_t is cleaned up when it is out of scope. - */ -struct instrlist_autoclean_t { -public: - instrlist_autoclean_t(void *drcontext, instrlist_t *data) - : drcontext(drcontext) - , data(data) - { - } - ~instrlist_autoclean_t() - { -#ifdef DEBUG - if (drcontext == nullptr) { - std::cerr << "instrlist_autoclean_t: invalid drcontext" << std::endl; - exit(1); - } -#endif - if (data != nullptr) { - instrlist_clear_and_destroy(drcontext, data); - data = nullptr; - } - } - void *drcontext = nullptr; - instrlist_t *data = nullptr; -}; - /** * The type of pt2ir_t::convert() return value. */ @@ -259,6 +231,18 @@ public: */ std::string kcore_path; + /** + * The path of the kernel image file. The kernel image file is used to decode the + * kernel PT raw trace. This file contains all code segments in kcore. + */ + std::string kernel_image_path; + + /** + * The path of the kernel image metadata file. The metadata file contains the metadata + * of the code segments in kernel_image_path. + */ + std::string kernel_image_metadata_path; + pt2ir_config_t() { raw_file_path = ""; @@ -267,6 +251,8 @@ public: sb_primary_file_path = ""; sb_secondary_file_path_list.clear(); kcore_path = ""; + kernel_image_path = ""; + kernel_image_metadata_path = ""; pt_config.cpu.vendor = CPU_VENDOR_UNKNOWN; pt_config.cpu.family = 0; pt_config.cpu.model = 0; @@ -375,7 +361,12 @@ private: * information. */ bool - load_kernel_image(IN std::string &path); + load_kcore(IN std::string &path); + + /* Load the all code segments in kernel image to pt_insn_decoder's image cache. + */ + bool + load_kernel_image(IN std::string &path, IN std::string &metadata_path); /* Allocate a sideband decoder in the sideband session. The sideband session may * allocate many decoders, which mainly work on handling sideband perf records and diff --git a/clients/drcachesim/tests/offline-kernel-simple.templatex b/clients/drcachesim/tests/offline-kernel-simple.templatex index 0769882703344fdc57b03b8f71cc800704d7f690..ff8307124db3170fe8618bc2500d3c61d0150267 100644 --- a/clients/drcachesim/tests/offline-kernel-simple.templatex +++ b/clients/drcachesim/tests/offline-kernel-simple.templatex @@ -1,3 +1,28 @@ Hello, world! -.* +Cache simulation results: +Core #0 \(1 thread\(s\)\) + L1I stats: + Hits: *[0-9,\.]* + Misses: *[0-9,\.]* + Compulsory misses: *[0-9,\.]* + Invalidations: *0 +[\s\S]*Miss rate: *[0-9][,\.]..% + L1D stats: + Hits: *[0-9,\.]* + Misses: *[0-9,\.]* + Compulsory misses: *[0-9,\.]* + Invalidations: *0 +[\s\S]*Miss rate: *[0-9][,\.]..% +Core #1 \(0 thread\(s\)\) +Core #2 \(0 thread\(s\)\) +Core #3 \(0 thread\(s\)\) +LL stats: + Hits: *[0-9,\.]* + Misses: *[0-9,\.]* + Compulsory misses: *[0-9,\.]* + Invalidations: *0 +[\s\S]*Local miss rate: *[0-9,.]*% + Child hits: *[0-9,\.]* + Total miss rate: *[0-4][,\.]..% +[\s\S]* Number of Instructions: *[0-9]* diff --git a/clients/drcachesim/tracer/kernel_image.cpp b/clients/drcachesim/tracer/kernel_image.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9c331aba6754016dcd471294254cb3f7c700824 --- /dev/null +++ b/clients/drcachesim/tracer/kernel_image.cpp @@ -0,0 +1,363 @@ +/* ********************************************************** + * Copyright (c) 2022 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 VMWARE, 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. + */ + +#include <elf.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> +#include <fstream> + +#include "../common/utils.h" +#include "dr_api.h" +#include "kernel_image.h" + +#define MODULES_FILE_PATH "/proc/modules" +#define KALLSYMS_FILE_PATH "/proc/kallsyms" +#define KCORE_FILE_PATH "/proc/kcore" +#define KERNEL_IMAGE_FILE_NAME "kimage" +#define KERNEL_IMAGE_METADATA_FILE_NAME "kimage.metadata" + +#define MODULE_NEME_MAX_LEN 100 +#define SYMBOL_MAX_LEN 300 + +/* This struct type defines the information of every module read from /proc/module. + * We store all information on a linked list. + */ +struct proc_module_t { + proc_module_t *next; + char name[MODULE_NEME_MAX_LEN]; + uint64_t start; + uint64_t end; +}; + +/* This struct type defines the metadata of every code segment read from /proc/kcore. + * We store all metadata on a linked list. + */ +struct proc_kcore_code_segment_t { + proc_kcore_code_segment_t *next; + + /* The start offset of the code segment. */ + uint64_t start; + + /* The length of the code segment. */ + ssize_t len; + + /* The base address of the code segment. */ + uint64_t base; +}; + +kernel_image_t::kernel_image_t() + : modules_(nullptr) + , kcore_code_segments_(nullptr) +{ +} + +kernel_image_t::~kernel_image_t() +{ + /* Free the module information linked list. */ + proc_module_t *module = modules_; + while (module) { + proc_module_t *next = module->next; + dr_global_free(module, sizeof(proc_module_t)); + module = next; + } + modules_ = nullptr; + + /* Free the kcore code segment metadata linked list. */ + proc_kcore_code_segment_t *kcore_code_segment = kcore_code_segments_; + while (kcore_code_segment) { + proc_kcore_code_segment_t *next = kcore_code_segment->next; + dr_global_free(kcore_code_segment, sizeof(proc_kcore_code_segment_t)); + kcore_code_segment = next; + } + kcore_code_segments_ = nullptr; +} + +bool +kernel_image_t::read_modules() +{ + std::ifstream f(MODULES_FILE_PATH, std::ios::in); + if (!f.is_open()) { + ASSERT(false, "failed to open " MODULES_FILE_PATH); + return false; + } + proc_module_t *last_module = modules_; + std::string line; + while (std::getline(f, line)) { + /* Each line is similar to the following line: + * 'scsi_dh_hp_sw 12895 0 - Live 0xffffffffa005e000' + * We parse the first, the second, and the last field to construct a proc_module_t + * type instance. + */ + char mname[MODULE_NEME_MAX_LEN]; + uint64_t addr; + int len; + if (dr_sscanf(line.c_str(), "%99s %d %*d %*s %*s " HEX64_FORMAT_STRING, mname, + &len, &addr) != 3) { + ASSERT(false, "failed to parse " MODULES_FILE_PATH); + f.close(); + return false; + } + + proc_module_t *module = (proc_module_t *)dr_global_alloc(sizeof(proc_module_t)); + dr_snprintf(module->name, BUFFER_SIZE_ELEMENTS(module->name), "%s", mname); + NULL_TERMINATE_BUFFER(module->name); + module->start = addr; + module->end = addr + len; + module->next = nullptr; + if (last_module == nullptr) { + modules_ = module; + last_module = module; + } else { + last_module->next = module; + last_module = module; + } + } + f.close(); + return true; +} + +bool +kernel_image_t::read_kallsyms() +{ + std::ifstream f(KALLSYMS_FILE_PATH, std::ios::in); + if (!f.is_open()) { + ASSERT(false, "failed to open " KALLSYMS_FILE_PATH); + return false; + } + proc_module_t *kernel_module = nullptr; + std::string line; + while (std::getline(f, line)) { + char name[SYMBOL_MAX_LEN]; + uint64_t addr; + if (dr_sscanf(line.c_str(), HEX64_FORMAT_STRING " %*1c %299s [%*99s", &addr, + name) < 2) + continue; + if (strcmp(name, "_stext") == 0) { + if (kernel_module != nullptr) { + f.close(); + return false; + } + kernel_module = (proc_module_t *)dr_global_alloc(sizeof(proc_module_t)); + dr_snprintf(kernel_module->name, BUFFER_SIZE_ELEMENTS(kernel_module->name), + "kernel"); + NULL_TERMINATE_BUFFER(kernel_module->name); + kernel_module->start = addr; + } else if (!strcmp(name, "_etext")) { + if (kernel_module == nullptr) { + f.close(); + return false; + } + kernel_module->end = addr; + kernel_module->next = modules_; + modules_ = kernel_module; + kernel_module = nullptr; + } + } + f.close(); + return true; +} + +bool +kernel_image_t::read_kcore() +{ + file_t fd = dr_open_file(KCORE_FILE_PATH, DR_FILE_READ); + if (fd < 0) { + ASSERT(false, "failed to open" KCORE_FILE_PATH); + return false; + } + + uint8_t e_ident[EI_NIDENT]; + if (dr_read_file(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident)) { + ASSERT(false, "failed to read the e_ident array of " KCORE_FILE_PATH); + dr_close_file(fd); + return false; + } + + for (int idx = 0; idx < SELFMAG; ++idx) { + if (e_ident[idx] != ELFMAG[idx]) { + ASSERT(false, KCORE_FILE_PATH " is not an ELF file"); + dr_close_file(fd); + return false; + } + } + if (e_ident[EI_CLASS] != ELFCLASS64) { + ASSERT(false, KCORE_FILE_PATH " is not a 64-bit ELF file"); + dr_close_file(fd); + return false; + } + + /* Read phdrs from kcore. */ + if (!dr_file_seek(fd, 0, DR_SEEK_SET)) { + ASSERT(false, "failed to seek to the begin of " KCORE_FILE_PATH); + dr_close_file(fd); + return false; + } + + Elf64_Ehdr ehdr; + if (dr_read_file(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) { + ASSERT(false, "failed to read the ehdr of " KCORE_FILE_PATH); + dr_close_file(fd); + return false; + } + if (LONG_MAX < ehdr.e_phoff) { + ASSERT(false, "the ELF header of " KCORE_FILE_PATH " is too big"); + dr_close_file(fd); + return false; + } + + if (!dr_file_seek(fd, (long)ehdr.e_phoff, DR_SEEK_SET)) { + ASSERT(false, "failed to seek the program header's end of " KCORE_FILE_PATH); + dr_close_file(fd); + return false; + } + + for (Elf64_Half pidx = 0; pidx < ehdr.e_phnum; ++pidx) { + Elf64_Phdr phdr; + if (dr_read_file(fd, &phdr, sizeof(phdr)) != sizeof(phdr)) { + ASSERT(false, "failed to read the Phdr of " KCORE_FILE_PATH); + dr_close_file(fd); + return false; + } + + if (phdr.p_type != PT_LOAD || phdr.p_filesz == 0) + continue; + /* Read code segment metadata from kcore. */ + proc_module_t *module = modules_; + if (module == nullptr) { + ASSERT(false, "no module found in " MODULES_FILE_PATH); + dr_close_file(fd); + return false; + } + proc_kcore_code_segment_t *last_kcore_code_segment = kcore_code_segments_; + while (module != nullptr) { + if (module->start >= phdr.p_vaddr && + module->end < phdr.p_vaddr + phdr.p_filesz) { + proc_kcore_code_segment_t *kcore_code_segment = + (proc_kcore_code_segment_t *)dr_global_alloc( + sizeof(proc_kcore_code_segment_t)); + kcore_code_segment->start = module->start - phdr.p_vaddr + phdr.p_offset; + kcore_code_segment->len = module->end - module->start; + kcore_code_segment->base = module->start; + kcore_code_segment->next = nullptr; + if (last_kcore_code_segment == nullptr) { + kcore_code_segments_ = kcore_code_segment; + last_kcore_code_segment = kcore_code_segment; + } else { + last_kcore_code_segment->next = kcore_code_segment; + last_kcore_code_segment = kcore_code_segment; + } + } + module = module->next; + } + } + + dr_close_file(fd); + return true; +} + +bool +kernel_image_t::init() +{ + if (!read_modules()) { + return false; + } + if (!read_kallsyms()) { + return false; + } + if (!read_kcore()) { + return false; + } + return true; +} + +bool +kernel_image_t::dump(const char *to_dir) +{ + file_t kcore_fd = dr_open_file(KCORE_FILE_PATH, DR_FILE_READ); + if (kcore_fd < 0) { + ASSERT(false, "failed to open " KCORE_FILE_PATH); + return false; + } + + char image_file_name[MAXIMUM_PATH]; + char metadata_file_name[MAXIMUM_PATH]; + dr_snprintf(image_file_name, BUFFER_SIZE_ELEMENTS(image_file_name), "%s%s%s", to_dir, + DIRSEP, KERNEL_IMAGE_FILE_NAME); + NULL_TERMINATE_BUFFER(image_file_name); + dr_snprintf(metadata_file_name, BUFFER_SIZE_ELEMENTS(metadata_file_name), "%s%s%s", + to_dir, DIRSEP, KERNEL_IMAGE_METADATA_FILE_NAME); + NULL_TERMINATE_BUFFER(metadata_file_name); + file_t image_fd = dr_open_file(image_file_name, DR_FILE_WRITE_OVERWRITE); + if (image_fd < 0) { + ASSERT(false, "failed to open kernel image file"); + dr_close_file(kcore_fd); + return false; + } + std::ofstream metadata_fd(metadata_file_name, std::ios::out); + if (!metadata_fd.is_open()) { + ASSERT(false, "failed to open kernel image metadata file"); + dr_close_file(image_fd); + dr_close_file(kcore_fd); + return false; + } + + uint64_t offset = 0; + bool dump_success = true; + proc_kcore_code_segment_t *kcore_code_segment = kcore_code_segments_; + while (kcore_code_segment != nullptr) { + dr_file_seek(kcore_fd, kcore_code_segment->start, DR_SEEK_SET); + char *buf = (char *)dr_global_alloc(kcore_code_segment->len); + if (dr_read_file(kcore_fd, buf, kcore_code_segment->len) != + kcore_code_segment->len) { + ASSERT(false, "failed to read " KCORE_FILE_PATH); + dump_success = false; + break; + } + if (dr_write_file(image_fd, buf, kcore_code_segment->len) != + kcore_code_segment->len) { + ASSERT(false, "failed to write code segment to kernel image file"); + dump_success = false; + break; + } + dr_global_free(buf, kcore_code_segment->len); + metadata_fd << std::hex << offset << " " << kcore_code_segment->len << " " + << kcore_code_segment->base << std::endl; + offset += kcore_code_segment->len; + kcore_code_segment = kcore_code_segment->next; + } + + metadata_fd.close(); + dr_close_file(image_fd); + dr_close_file(kcore_fd); + return dump_success; +} diff --git a/clients/drcachesim/tracer/kernel_image.h b/clients/drcachesim/tracer/kernel_image.h new file mode 100644 index 0000000000000000000000000000000000000000..8bde07db9f8e364c8a17141e53c5c5ca7f939038 --- /dev/null +++ b/clients/drcachesim/tracer/kernel_image.h @@ -0,0 +1,89 @@ +/* ********************************************************** + * Copyright (c) 2022 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 VMWARE, 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. + */ + +/* kernel_image.h: header of module for dump kernel code segments to one file. + * This is only for Linux x86_64. + */ + +#ifndef _KCORE_H_ +#define _KCORE_H_ 1 + +struct proc_module_t; +struct proc_kcore_code_segment_t; + +/* This class is used to dump kernel code segments to one file. + */ +class kernel_image_t { +public: + kernel_image_t(); + ~kernel_image_t(); + + /* Parse the kernel code segments from /proc/kcore. + * This function will first read modules from /proc/modules, then read all kernel + * symbols from /proc/kallsyms. Then it will parse the kernel code segments from + * /proc/kcore. + */ + bool + init(); + + /* Dump the kernel code segments to one file. + * This function will dump all kernel code segments to one file called kimage and dump + * the metadata of every code segment to kimage.metadata. + */ + bool + dump(const char *to_dir); + +private: + /* Read the module information to module list from /proc/modules. + */ + bool + read_modules(); + + /* Parse the kernel module information from /proc/kallsyms and insert them to the + * start of module list. + */ + bool + read_kallsyms(); + + /* Read the kernel code segments from /proc/kcore. + */ + bool + read_kcore(); + + /* The module list. */ + proc_module_t *modules_; + + /* The kernel code segment metadata list. */ + proc_kcore_code_segment_t *kcore_code_segments_; +}; + +#endif /* _KCORE_H_ */ diff --git a/clients/drcachesim/tracer/raw2trace.cpp b/clients/drcachesim/tracer/raw2trace.cpp index d3e40a151c55cb23d49cd5afcfd3d62920bf0a00..efa63bbee1b2d31e15d738740ba646a0a399974e 100644 --- a/clients/drcachesim/tracer/raw2trace.cpp +++ b/clients/drcachesim/tracer/raw2trace.cpp @@ -1296,13 +1296,20 @@ raw2trace_t::get_file_type(void *tls) return tdata->file_type; } +std::string +raw2trace_t::get_syscall_pt_trace_dir(void *tls) +{ + auto tdata = reinterpret_cast<raw2trace_thread_data_t *>(tls); + return tdata->syscall_pt_trace_dir; +} + raw2trace_t::raw2trace_t(const char *module_map, const std::vector<std::istream *> &thread_files, const std::vector<std::ostream *> &out_files, const std::vector<archive_ostream_t *> &out_archives, file_t encoding_file, void *dcontext, unsigned int verbosity, - int worker_count, const std::string &alt_module_dir, - uint64_t chunk_instr_count) + int worker_count, const std::string &syscall_pt_trace_dir, + const std::string &alt_module_dir, uint64_t chunk_instr_count) : trace_converter_t(dcontext) , worker_count_(worker_count) , user_process_(nullptr) @@ -1310,6 +1317,7 @@ raw2trace_t::raw2trace_t(const char *module_map, , modmap_(module_map) , encoding_file_(encoding_file) , verbosity_(verbosity) + , syscall_pt_trace_dir_(syscall_pt_trace_dir) , alt_module_dir_(alt_module_dir) , chunk_instr_count_(chunk_instr_count) { @@ -1342,6 +1350,7 @@ raw2trace_t::raw2trace_t(const char *module_map, thread_data_[i].out_archive = nullptr; thread_data_[i].out_file = out_files[i]; } + thread_data_[i].syscall_pt_trace_dir = syscall_pt_trace_dir_; } // Since we know the traced-thread count up front, we use a simple round-robin // static work assigment. This won't be as load balanced as a dynamic work diff --git a/clients/drcachesim/tracer/raw2trace.h b/clients/drcachesim/tracer/raw2trace.h index 13939c62ada0c1fe57b34b7a155f84e87ff4b19f..77314f6e385fdd8e7c278774e248f76b71ad3028 100644 --- a/clients/drcachesim/tracer/raw2trace.h +++ b/clients/drcachesim/tracer/raw2trace.h @@ -44,6 +44,10 @@ #include "dr_api.h" #include "drmemtrace.h" #include "drcovlib.h" +#ifdef BUILD_PT_POST_PROCESSOR +# include "../drpt2trace/pt2ir.h" +# include "../drpt2trace/ir2trace.h" +#endif #include <array> #include <atomic> #include <memory> @@ -73,7 +77,7 @@ # define OUTFILE_SUFFIX_LZ4 "raw.lz4" #endif #define OUTFILE_SUBDIR "raw" -#ifdef BUILD_PT_TRACER +#if defined(BUILD_PT_TRACER) || defined(BUILD_PT_POST_PROCESSOR) # define KERNEL_PT_OUTFILE_SUBDIR "kernel.raw" #endif #define WINDOW_SUBDIR_PREFIX "window" @@ -799,6 +803,16 @@ protected: buf, (trace_marker_type_t)in_entry->extended.valueB, marker_val); if (in_entry->extended.valueB == TRACE_MARKER_TYPE_KERNEL_EVENT) { impl()->log(4, "Signal/exception between bbs\n"); + } else if (in_entry->extended.valueB == TRACE_MARKER_TYPE_SYSCALL_ID) { +#ifdef BUILD_PT_POST_PROCESSOR + std::string error = impl()->write(tls, buf_base, + reinterpret_cast<trace_entry_t *>(buf)); + if (!error.empty()) + return error; + append_syscall_pt_trace(tls, tid, marker_val); + buf_base = impl()->get_write_buffer(tls); + buf = reinterpret_cast<byte *>(buf_base); +#endif } impl()->log(3, "Appended marker type %u value " PIFX "\n", (trace_marker_type_t)in_entry->extended.valueB, @@ -1454,6 +1468,78 @@ private: return ""; } +#ifdef BUILD_PT_POST_PROCESSOR +# define PT_DATA_FILE_NAME_SUFFIX ".pt" +# define PT_METADAT_FILE_NAME_SUFFIX ".metadata" +# define KERNEL_IMAGE_FILE_NAME "kimage" +# define KERNEL_IMAGE_METADATA_FILE_NAME "kimage.metadata" + std::string + append_syscall_pt_trace(void *tls, thread_id_t thread_id, uint64_t syscall_id) + { + std::string error = ""; + pt2ir_config_t config = {}; + config.raw_file_path = impl()->get_syscall_pt_trace_dir(tls) + "/" + + std::to_string(thread_id) + "." + std::to_string(syscall_id) + + PT_DATA_FILE_NAME_SUFFIX; + config.kernel_image_path = + impl()->get_syscall_pt_trace_dir(tls) + "/" + KERNEL_IMAGE_FILE_NAME; + config.kernel_image_metadata_path = + impl()->get_syscall_pt_trace_dir(tls) + "/" + KERNEL_IMAGE_METADATA_FILE_NAME; + config.init_with_metadata(config.raw_file_path + PT_METADAT_FILE_NAME_SUFFIX); + + std::unique_ptr<pt2ir_t> ptconverter(new pt2ir_t()); + if (!ptconverter->init(config)) { + error = "Failed to initialize pt2ir"; + return error; + } + instrlist_autoclean_t drir = { GLOBAL_DCONTEXT, nullptr }; + pt2ir_convert_status_t pt2ir_convert_status = ptconverter->convert(drir); + if (pt2ir_convert_status != PT2IR_CONV_SUCCESS) { + error = "Failed to convert PT raw trace to DR IR"; + return error; + } + if (drir.data == nullptr) { + dr_printf("No DR IR instructions to append %d\n", syscall_id); + return ""; + } + + std::vector<trace_entry_t> entries; + ir2trace_convert_status_t ir2trace_convert_status = + ir2trace_t::convert(drir, entries); + if (ir2trace_convert_status != IR2TRACE_CONV_SUCCESS) { + error = "Failed to convert DR IR to trace entries"; + return error; + } + + trace_entry_t *buf_base = impl()->get_write_buffer(tls); + byte *buf = reinterpret_cast<byte *>(buf_base); + for (auto &entry : entries) { + trace_entry_t *buf_entry = reinterpret_cast<trace_entry_t *>(buf); + *buf_entry = entry; + buf += sizeof(trace_entry_t); + size_t size = reinterpret_cast<trace_entry_t *>(buf) - buf_base; + if (size >= WRITE_BUFFER_SIZE) { + error = impl()->write(tls, buf_base, + reinterpret_cast<trace_entry_t *>(buf)); + if (!error.empty()) + break; + impl()->log(4, "Appended %u entries to write buffer\n", size); + buf_base = impl()->get_write_buffer(tls); + buf = reinterpret_cast<byte *>(buf_base); + } + } + + if (error.empty()) { + error = impl()->write(tls, buf_base, reinterpret_cast<trace_entry_t *>(buf)); + if (!error.empty()) + return error; + impl()->log(4, "Appended %u entries to write buffer\n", + reinterpret_cast<trace_entry_t *>(buf) - buf_base); + } + return ""; + } +#endif + std::string get_marker_value(void *tls, INOUT const offline_entry_t **entry, OUT uintptr_t *value) { @@ -1648,6 +1734,7 @@ public: const std::vector<archive_ostream_t *> &out_archives, file_t encoding_file = INVALID_FILE, void *dcontext = nullptr, unsigned int verbosity = 0, int worker_count = -1, + const std::string &syscall_pt_trace_dir = "", const std::string &alt_module_dir = "", uint64_t chunk_instr_count = 10 * 1000 * 1000); virtual ~raw2trace_t(); @@ -1778,6 +1865,7 @@ protected: , last_decode_modidx(0) , last_decode_modoffs(0) , last_block_summary(nullptr) + , syscall_pt_trace_dir("") { } @@ -1809,6 +1897,7 @@ protected: uint64 last_decode_modidx; uint64 last_decode_modoffs; block_summary_t *last_block_summary; + std::string syscall_pt_trace_dir; uint64 last_window = 0; // Statistics on the processing. @@ -1893,6 +1982,8 @@ private: add_to_statistic(void *tls, raw2trace_statistic_t stat, int value); void log_instruction(app_pc decode_pc, app_pc orig_pc); + std::string + get_syscall_pt_trace_dir(void *tls); std::string append_delayed_branch(void *tls); @@ -2015,6 +2106,8 @@ private: unsigned int verbosity_ = 0; + std::string syscall_pt_trace_dir_; + std::string alt_module_dir_; // Our decode_cache duplication will not scale forever on very large code diff --git a/clients/drcachesim/tracer/raw2trace_directory.cpp b/clients/drcachesim/tracer/raw2trace_directory.cpp index 614432c35244abdeeac011b062a042e8a4ce0f66..ac0475a8776c7ae4ab8d5d62ba99c404657613f4 100644 --- a/clients/drcachesim/tracer/raw2trace_directory.cpp +++ b/clients/drcachesim/tracer/raw2trace_directory.cpp @@ -395,6 +395,16 @@ raw2trace_directory_t::initialize_funclist_file( return ""; } +std::string +raw2trace_directory_t::get_syscall_pt_trace_dir() +{ +#ifdef BUILD_PT_POST_PROCESSOR + return indir_ + "/../" + KERNEL_PT_OUTFILE_SUBDIR; +#else + return ""; +#endif +} + raw2trace_directory_t::~raw2trace_directory_t() { if (modfile_bytes_ != nullptr) diff --git a/clients/drcachesim/tracer/raw2trace_directory.h b/clients/drcachesim/tracer/raw2trace_directory.h index e1aac03f91b520114133470aac50a91ff194adf6..7e6327561eb7bae26e5c660f3215bc43785fba76 100644 --- a/clients/drcachesim/tracer/raw2trace_directory.h +++ b/clients/drcachesim/tracer/raw2trace_directory.h @@ -86,6 +86,9 @@ public: std::vector<std::ostream *> out_files_; std::vector<archive_ostream_t *> out_archives_; + std::string + get_syscall_pt_trace_dir(); + private: std::string read_module_file(const std::string &modfilename); diff --git a/clients/drcachesim/tracer/raw2trace_launcher.cpp b/clients/drcachesim/tracer/raw2trace_launcher.cpp index 6aafedd3be9b4f53f95d14bffe99a6b0d6c35f86..8acb14860f4bbf862c2cf3f457bf0ca0e92831d5 100644 --- a/clients/drcachesim/tracer/raw2trace_launcher.cpp +++ b/clients/drcachesim/tracer/raw2trace_launcher.cpp @@ -108,10 +108,11 @@ _tmain(int argc, const TCHAR *targv[]) std::string dir_err = dir.initialize(op_indir.get_value(), op_outdir.get_value()); if (!dir_err.empty()) FATAL_ERROR("Directory parsing failed: %s", dir_err.c_str()); - raw2trace_t raw2trace( - dir.modfile_bytes_, dir.in_files_, dir.out_files_, dir.out_archives_, - dir.encoding_file_, nullptr, op_verbose.get_value(), op_jobs.get_value(), - op_alt_module_dir.get_value(), op_chunk_instr_count.get_value()); + raw2trace_t raw2trace(dir.modfile_bytes_, dir.in_files_, dir.out_files_, + dir.out_archives_, dir.encoding_file_, nullptr, + op_verbose.get_value(), op_jobs.get_value(), + dir.get_syscall_pt_trace_dir(), op_alt_module_dir.get_value(), + op_chunk_instr_count.get_value()); std::string error = raw2trace.do_conversion(); if (!error.empty()) FATAL_ERROR("Conversion failed: %s", error.c_str()); diff --git a/clients/drcachesim/tracer/syscall_pt_trace.cpp b/clients/drcachesim/tracer/syscall_pt_trace.cpp index 7071d0e9d0273351702edb537294401b56bbcd9e..034272b71b764f22b20feee5dcc6d4f48d8bd36d 100644 --- a/clients/drcachesim/tracer/syscall_pt_trace.cpp +++ b/clients/drcachesim/tracer/syscall_pt_trace.cpp @@ -38,6 +38,7 @@ #include "../../../core/unix/include/syscall_linux_x86.h" #include "dr_api.h" #include "drpttracer.h" +#include "kernel_image.h" #include "syscall_pt_trace.h" #ifndef BUILD_PT_TRACER @@ -65,10 +66,9 @@ syscall_pt_trace_t::~syscall_pt_trace_t() bool syscall_pt_trace_t::init(void *drcontext, char *pt_dir_name, size_t pt_dir_name_size, - file_t (*open_file_func)(const char *fname, uint mode_flags), - ssize_t (*write_file_func)(file_t file, const void *data, - size_t count), - void (*close_file_func)(file_t file)) + drmemtrace_open_file_func_t open_file_func, + drmemtrace_write_file_func_t write_file_func, + drmemtrace_close_file_func_t close_file_func) { drcontext_ = drcontext; memcpy(pt_dir_name_, pt_dir_name, pt_dir_name_size); @@ -162,43 +162,25 @@ syscall_pt_trace_t::trace_data_dump(drpttracer_output_autoclean_t &output) } bool -syscall_pt_trace_t::kernel_image_dump(IN const char *to_dir) +syscall_pt_trace_t::is_syscall_pt_trace_enabled(IN int sysnum) { - /* TODO i#5505: Using the perf command to get kcore and kallsyms is not a good - * solution. There are three issues: - * 1. The perf may not be installed on the system. - * 2. The $PATH of system may not contain the perf command. - * 3. The following script will clobber/remove the user's local data. - * These issues may cause unexpected behaviors. - * - * We need to implement the kcore_copy() in syscall_pt_trace_t. + /* The following syscall's post syscall callback can't be triggered. So we don't + * support to recording the kernel PT of them. */ -#define SHELLSCRIPT_FMT \ - "perf record --kcore -e intel_pt/cyc,noretcomp/k echo '' >/dev/null 2>&1 \n" \ - "chmod 755 -R perf.data \n" \ - "cp perf.data/kcore_dir/kcore %s/ \n" \ - "cp perf.data/kcore_dir/kallsyms %s/ \n" \ - "rm -rf perf.data \n" -#define SHELLSCRIPT_MAX_LEN 512 + MAXIMUM_PATH * 2 - char shellscript[SHELLSCRIPT_MAX_LEN]; - dr_snprintf(shellscript, BUFFER_SIZE_ELEMENTS(shellscript), SHELLSCRIPT_FMT, to_dir, - to_dir); - NULL_TERMINATE_BUFFER(shellscript); - int ret = system(shellscript); - if (ret != 0) { - ASSERT(false, "failed to run shellscript to dump kcore and kallsyms"); + if (sysnum == SYS_exit || sysnum == SYS_exit_group || sysnum == SYS_execve) { return false; } return true; } bool -syscall_pt_trace_t::is_syscall_pt_trace_enabled(IN int sysnum) +syscall_pt_trace_t::kernel_image_dump(IN const char *to_dir) { - /* The following syscall's post syscall callback can't be triggered. So we don't - * support to recording the kernel PT of them. - */ - if (sysnum == SYS_exit || sysnum == SYS_exit_group || sysnum == SYS_execve) { + std::unique_ptr<kernel_image_t> kernel_image(new kernel_image_t()); + if (!kernel_image->init()) { + return false; + } + if (!kernel_image->dump(to_dir)) { return false; } return true; diff --git a/clients/drcachesim/tracer/syscall_pt_trace.h b/clients/drcachesim/tracer/syscall_pt_trace.h index ecf4221e0d4ca70480e16e6773ef71eb790f6aad..c5cac4dc143bf3a04814163cabe7a73758ac6faf 100644 --- a/clients/drcachesim/tracer/syscall_pt_trace.h +++ b/clients/drcachesim/tracer/syscall_pt_trace.h @@ -40,6 +40,7 @@ #include "../common/utils.h" #include "dr_api.h" +#include "drmemtrace.h" #include "drpttracer.h" /* The auto cleanup wrapper of pttracer handle. @@ -108,9 +109,9 @@ public: */ bool init(void *drcontext, char *pt_dir_name, size_t pt_dir_name_size, - file_t (*open_file_func)(const char *fname, uint mode_flags), - ssize_t (*write_file_func)(file_t file, const void *data, size_t count), - void (*close_file_func)(file_t file)); + drmemtrace_open_file_func_t open_file_func, + drmemtrace_write_file_func_t write_file_func, + drmemtrace_close_file_func_t close_file_func); /* Start the PT tracing for current syscall and store the sysnum of the syscall. */ bool @@ -153,13 +154,13 @@ private: trace_data_dump(drpttracer_output_autoclean_t &output); /* The shared file open function. */ - file_t (*open_file_func_)(const char *fname, uint mode_flags); + drmemtrace_open_file_func_t open_file_func_; /* The shared file write function. */ - ssize_t (*write_file_func_)(file_t file, const void *data, size_t count); + drmemtrace_write_file_func_t write_file_func_; /* The shared file close function. */ - void (*close_file_func_)(file_t file); + drmemtrace_close_file_func_t close_file_func_; /* The pttracer handle held by this instance. */ drpttracer_handle_autoclean_t pttracer_handle_; diff --git a/clients/drcachesim/tracer/tracer.cpp b/clients/drcachesim/tracer/tracer.cpp index 6ba64481bdf86bbd69bdf866106eeb709c39a638..122f5a3a6802a73531f13bbc42baf939630961a7 100644 --- a/clients/drcachesim/tracer/tracer.cpp +++ b/clients/drcachesim/tracer/tracer.cpp @@ -316,6 +316,7 @@ instrumentation_exit() { dr_unregister_filter_syscall_event(event_filter_syscall); if (!drmgr_unregister_pre_syscall_event(event_pre_syscall) || + !drmgr_unregister_post_syscall_event(event_post_syscall) || !drmgr_unregister_kernel_xfer_event(event_kernel_xfer) || !drmgr_unregister_bb_app2app_event(event_bb_app2app)) DR_ASSERT(false); @@ -1285,6 +1286,10 @@ event_post_syscall(void *drcontext, int sysnum) /* Write a marker to userspace raw trace. */ if (BUF_PTR(data->seg_base) == NULL) return; /* This thread was filtered out. */ + // if (data->syscall_pt_trace.get_last_recorded_syscall_id() < 200) { + // dr_printf("sysnum %d, id %d \n", sysnum, data->syscall_pt_trace.get_last_recorded_syscall_id()); + // } + // dr_printf("sysnum %d, id %d \n", sysnum, data->syscall_pt_trace.get_last_recorded_syscall_id()); trace_marker_type_t marker_type = TRACE_MARKER_TYPE_SYSCALL_ID; uintptr_t marker_val = data->syscall_pt_trace.get_last_recorded_syscall_id(); BUF_PTR(data->seg_base) += @@ -1521,9 +1526,13 @@ event_exit(void) #ifdef BUILD_PT_TRACER if (op_offline.get_value() && op_enable_kernel_tracing.get_value()) { drpttracer_exit(); - /* Dump kcore and kallsyms to {kernel_pt_logsubdir}. */ + /* Dump kcore to kimage and kimage.metadata, and store the two file to + * {kernel_pt_logsubdir}. + * The Kernel dump function is only invoked at processes exit on the Linux X86_64 + * platform. + */ if (!syscall_pt_trace_t::kernel_image_dump(kernel_pt_logsubdir)) { - NOTIFY(0, "WARNING: failed to run shellscript to dump kernel image\n"); + NOTIFY(0, "WARNING: failed to dump kernel image\n"); } } #endif diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index 51a9bfa49da0f10b24e7aca1a0a8ac53667b5e8a..24c8a48e653e8dca7508c1e4d5c703aecd6c3e0f 100644 --- a/suite/tests/CMakeLists.txt +++ b/suite/tests/CMakeLists.txt @@ -3837,6 +3837,10 @@ if (BUILD_CLIENTS) use_DynamoRIO_drmemtrace_tracer(tool.drcacheoff.gencode) target_link_libraries(tool.drcacheoff.gencode drmemtrace_raw2trace drmemtrace_analyzer) + if (BUILD_PT_POST_PROCESSOR) + # Add the directory containing libipt.so and libipt-sb.so to the linker search path. + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='${PROJECT_BINARY_DIR}/lib'") + endif (BUILD_PT_POST_PROCESSOR) target_include_directories(tool.drcacheoff.gencode PUBLIC "${PROJECT_SOURCE_DIR}/clients/drcachesim" "${PROJECT_SOURCE_DIR}/clients/drcachesim/common" @@ -4078,7 +4082,8 @@ if (BUILD_CLIENTS) if (proc_supports_pt) if (BUILD_PT_TRACER AND BUILD_PT_POST_PROCESSOR) get_target_path_for_execution(drpt2trace_path drpt2trace "${location_suffix}") - macro (torunonly_drcacheoff_kernel testname exetgt extra_ops app_args) + get_target_path_for_execution(drcachesim_path drcachesim "${location_suffix}") + macro (torunonly_drcacheoff_kernel testname exetgt extra_ops sim_atops app_args) set(testname_full "tool.drcacheoff.kernel.${testname}_SUDO") torunonly_ci(${testname_full} ${exetgt} drcachesim "offline-kernel-${testname}.c" # for templatex basename @@ -4094,10 +4099,12 @@ if (BUILD_CLIENTS) set(cmd_pfx "sudo@") set(${testname_full}_precmd "foreach@${cmd_pfx}${CMAKE_COMMAND}@-E@remove_directory@${testname_full}.*.dir") - set (${testname_full}_postcmd + set(${testname_full}_postcmd + "firstglob@${cmd_pfx}${drcachesim_path}@-indir@${testname_full}.*.dir${sim_atops}") + set (${testname_full}_postcmd2 "${cmd_pfx}chmod@777@-R@${testname_full}.*.dir") - set(${testname_full}_postcmd2 - "firstglob@${cmd_pfx}${drpt2trace_path}@-raw_pt@${testname_full}.*.dir/kernel.raw/*.1.pt@-elf@${testname_full}.*.dir/kernel.raw/kcore@-raw_pt_metadata@${testname_full}.*.dir/kernel.raw/*.1.pt.metadata") + set(${testname_full}_postcmd3 + "firstglob@${cmd_pfx}${drpt2trace_path}@-mode@DR@-raw_pt@${testname_full}.*.dir/kernel.raw/*.1.pt@-raw_pt_metadata@${testname_full}.*.dir/kernel.raw/*.1.pt.metadata@-kernel_image@${testname_full}.*.dir/kernel.raw/kimage@-kernel_image_metadata@${testname_full}.*.dir/kernel.raw/kimage.metadata") endmacro () torunonly_drcacheoff_kernel(simple ${ci_shared_app} "" "" "") endif (BUILD_PT_TRACER AND BUILD_PT_POST_PROCESSOR) @@ -4198,7 +4205,7 @@ endif (BUILD_CLIENTS) # We use the libipt/script/perf-get-opts.bash script to get the options for drpt2trace's tests. if (BUILD_CLIENTS) if (BUILD_PT_TRACER AND BUILD_PT_POST_PROCESSOR) - set(drpt2trace_commong "-print_trace" + set(drpt2trace_common_args "-print_trace" "-raw_pt" "${PROJECT_SOURCE_DIR}/clients/drcachesim/drpt2trace/test_simple.raw/pt.bin" "-pt_cpu_family" "6" @@ -4206,7 +4213,8 @@ if (BUILD_CLIENTS) "-pt_cpu_stepping" "4" "-pt_mtc_freq" "3" "-pt_nom_freq" "37" "-pt_cpuid_0x15_eax" "2" "-pt_cpuid_0x15_ebx" "308") - set(drpt2trace_sideband_args ${drpt2trace_commong} + set(drpt2trace_sideband_args ${drpt2trace_common_args} + "-mode" "SIDEBAND" "-primary_sb" "${PROJECT_SOURCE_DIR}/clients/drcachesim/drpt2trace/test_simple.raw/primary.sideband.pevent" "-secondary_sb" @@ -4220,7 +4228,8 @@ if (BUILD_CLIENTS) torunonly_api(tool.drpt2trace.sideband drpt2trace "../../clients/drcachesim/drpt2trace/test_simple.expect" "" "${drpt2trace_sideband_args}" ON OFF) - set(drpt2trace_elf_args ${drpt2trace_commong} + set(drpt2trace_elf_args ${drpt2trace_common_args} + "-mode" "ELF" "-elf" "${PROJECT_SOURCE_DIR}/clients/drcachesim/drpt2trace/test_simple.raw/hello") torunonly_api(tool.drpt2trace.elf drpt2trace