* Copyright (c) 2017-2023 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.
*/
* It uses ptrace to make sure raw2trace only interacts with the filesystem when expected
* -- it doesn't open/close any files outside of mapping modules (which should be local).
*
* XXX: We use ptrace as opposed to running under memtrace with replaced file operations
* because raw2trace uses drmodtrack, which doesn't isolate under static memtrace.
*/
#include "droption.h"
#include "tracer/raw2trace.h"
#include "tracer/raw2trace_directory.h"
#include <cassert>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <string>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <syscall.h>
#include <unistd.h>
namespace dynamorio {
namespace drmemtrace {
using ::dynamorio::droption::droption_parser_t;
using ::dynamorio::droption::DROPTION_SCOPE_ALL;
using ::dynamorio::droption::DROPTION_SCOPE_FRONTEND;
using ::dynamorio::droption::droption_t;
static droption_t<std::string> op_indir(DROPTION_SCOPE_FRONTEND, "indir", "",
"[Required] Directory with trace input files",
"Specifies a directory with raw files.");
#if defined(X64)
# define SYS_NUM(r) (r).orig_rax
#elif defined(X86)
# define SYS_NUM(r) (r).orig_eax
#else
# error "Test only supports x86"
#endif
#ifndef LINUX
# error "Test only supports Linux"
#endif
#define EXPECT(expr, msg) \
do { \
if (!(expr)) { \
std::cerr << "Error: " << msg << std::endl; \
return false; \
} \
} while (0)
#define REPORT(msg) \
do { \
std::cerr << msg << std::endl; \
} while (0)
bool
test_raw2trace(raw2trace_directory_t *dir)
{
pid_t pid = fork();
if (pid == -1) {
std::cerr << "Fork failed\n";
return false;
}
if (pid != 0) {
long res;
int status;
while (true) {
int wait_res = waitpid(pid, &status, __WALL);
if (wait_res < 0) {
if (errno == EINTR)
continue;
perror("Failed waiting");
return false;
}
if (WIFEXITED(status))
break;
user_regs_struct regs;
res = ptrace(PTRACE_GETREGS, pid, NULL, ®s);
if (res < 0) {
perror("ptrace failed");
return 1;
}
* on both.
*/
if (SYS_NUM(regs) == __NR_open || SYS_NUM(regs) == __NR_openat ||
SYS_NUM(regs) == __NR_creat) {
std::cerr << "open\n";
} else if (SYS_NUM(regs) == __NR_close) {
std::cerr << "close\n";
}
res = ptrace(PTRACE_SYSCALL, pid, NULL, 0);
if (res < 0) {
perror("ptrace failed");
return false;
}
}
if (WIFEXITED(status)) {
return true;
}
res = ptrace(PTRACE_DETACH, pid, NULL, NULL);
if (res < 0) {
perror("ptrace failed");
return false;
}
std::cerr << "Detached\n";
return true;
} else {
long res = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if (res < 0) {
perror("ptrace me failed");
return false;
}
raise(SIGSTOP);
* raw2trace::read_and_map_modules().
*/
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_,
GLOBAL_DCONTEXT, 1);
std::string error = raw2trace.do_conversion();
if (!error.empty()) {
std::cerr << "raw2trace failed " << error << "\n";
return false;
}
std::cerr << "Processed\n";
exit(0);
return false;
}
}
void
my_user_free(void *data)
{
REPORT("Custom user_free was called");
}
bool
test_module_mapper(const raw2trace_directory_t *dir)
{
std::unique_ptr<module_mapper_t> mapper = module_mapper_t::create(
dir->modfile_bytes_, nullptr, nullptr, nullptr, &my_user_free);
EXPECT(mapper->get_last_error().empty(), "Module mapper construction failed");
REPORT("About to load modules");
const auto &loaded_modules = mapper->get_loaded_modules();
EXPECT(!loaded_modules.empty(), "Expected module entries");
EXPECT(mapper->get_last_error().empty(), "Module loading failed");
REPORT("Loaded modules successfully");
bool found_simple_app = false;
for (const module_t &m : mapper->get_loaded_modules()) {
std::string path = m.path;
if (path.rfind("simple_app") != std::string::npos) {
found_simple_app = true;
break;
}
}
EXPECT(found_simple_app, "Expected app entry not found in module map");
REPORT("Successfully found app entry");
return true;
}
bool
test_trace_timestamp_reader(const raw2trace_directory_t *dir)
{
std::istream *file = dir->in_files_[0];
file->seekg(0);
offline_entry_t buffer[6];
file->read((char *)buffer, BUFFER_SIZE_BYTES(buffer));
std::string error;
if (!trace_metadata_reader_t::is_thread_start(buffer, &error, nullptr, nullptr) &&
!error.empty())
return false;
uint64 timestamp = 0;
if (drmemtrace_get_timestamp_from_offline_trace(buffer, BUFFER_SIZE_BYTES(buffer),
×tamp) != DRMEMTRACE_SUCCESS)
return false;
if (timestamp == 0)
return false;
REPORT("Read timestamp from thread header");
uint64 timestamp2 = 0;
if (drmemtrace_get_timestamp_from_offline_trace(buffer + 5, sizeof(offline_entry_t),
×tamp2) != DRMEMTRACE_SUCCESS)
return false;
if (timestamp != timestamp2)
return false;
REPORT("Read timestamp without thread header");
if (drmemtrace_get_timestamp_from_offline_trace(nullptr, 0, ×tamp2) ==
DRMEMTRACE_SUCCESS)
return false;
if (drmemtrace_get_timestamp_from_offline_trace(buffer, 0, ×tamp2) ==
DRMEMTRACE_SUCCESS)
return false;
if (drmemtrace_get_timestamp_from_offline_trace(buffer, sizeof(offline_entry_t),
×tamp2) == DRMEMTRACE_SUCCESS)
return false;
if (drmemtrace_get_timestamp_from_offline_trace(buffer, BUFFER_SIZE_BYTES(buffer),
nullptr) == DRMEMTRACE_SUCCESS)
return false;
if (timestamp != timestamp2)
return false;
REPORT("Verified boundary conditions");
offline_entry_t invalid_buffer[4];
memset(invalid_buffer, 0, BUFFER_SIZE_BYTES(invalid_buffer));
if (drmemtrace_get_timestamp_from_offline_trace(invalid_buffer,
BUFFER_SIZE_BYTES(invalid_buffer),
×tamp2) == DRMEMTRACE_SUCCESS)
return false;
if (timestamp != timestamp2)
return false;
REPORT("Verified invalid buffer");
return true;
}
int
test_main(int argc, const char *argv[])
{
std::string parse_err;
if (!droption_parser_t::parse_argv(DROPTION_SCOPE_FRONTEND, argc, (const char **)argv,
&parse_err, NULL) ||
op_indir.get_value().empty()) {
std::cerr << "Usage error: " << parse_err << "\nUsage:\n"
<< droption_parser_t::usage_short(DROPTION_SCOPE_ALL);
return 1;
}
* so they never get closed.
*/
raw2trace_directory_t *dir = new raw2trace_directory_t;
std::string dir_err = dir->initialize(op_indir.get_value(), "");
if (!dir_err.empty())
std::cerr << "Directory setup failed: " << dir_err;
bool test1_ret = test_raw2trace(dir);
bool test2_ret = test_module_mapper(dir);
bool test3_ret = test_trace_timestamp_reader(dir);
if (!(test1_ret && test2_ret && test3_ret))
return 1;
return 0;
}
}
}