Skip to content

Commit

Permalink
i#1729 offline traces: refactor drcachesim to simulate from a trace file
Browse files Browse the repository at this point in the history
Adds a new option -infile, which directs drcachesim to read a trace file
and simulate based on its contents.  Changes the launcher to bypass
application invocation when this option is selected.

Eliminates the shadowed iterator fields in simulator_t added by b11c906.

Adds reader setup code to analyzer_t which creates either an ipc_reader_t
or a file_reader_t depending on runtime options.  Changes the
analyzer_t/simulator_t model to initialize in the constructor rather than
using an init() function to provide automated invocation of this analyzer_t
reader setup code.  Success is cached and queried via operator!.

Updates cache_simulator_t and tlb_simulator_t for the new shared code in
analyzer_t and the new iterator fields.

Adds better handling of partially-initialized simulator objects in the
simulator destructors.

A test is forthcoming.

Review-URL: https://codereview.appspot.com/309440043
  • Loading branch information
derekbruening committed Sep 18, 2016
1 parent 2eb7494 commit 357da25
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 147 deletions.
1 change: 1 addition & 0 deletions clients/drcachesim/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ if (UNIX)

add_executable(drcachesim
launcher.cpp
analyzer.cpp
reader/reader.cpp
reader/file_reader.cpp
reader/ipc_reader.cpp
Expand Down
80 changes: 80 additions & 0 deletions clients/drcachesim/analyzer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/* **********************************************************
* Copyright (c) 2016 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 "analyzer.h"
#include "common/options.h"
#include "common/utils.h"
#include "reader/file_reader.h"
#include "reader/ipc_reader.h"

analyzer_t::analyzer_t() :
success(true)
{
// XXX: add a "required" flag to droption to avoid needing this here
if (op_infile.get_value().empty() && op_ipc_name.get_value().empty()) {
ERROR("Usage error: -ipc_name or -infile is required\nUsage:\n%s",
droption_parser_t::usage_short(DROPTION_SCOPE_ALL).c_str());
success = false;
return;
}
if (op_infile.get_value().empty()) {
trace_iter = new ipc_reader_t(op_ipc_name.get_value().c_str());
trace_end = new ipc_reader_t();
} else {
trace_iter = new file_reader_t(op_infile.get_value().c_str());
trace_end = new file_reader_t();
}
// We can't call trace_iter->init() here as it blocks for ipc_reader_t.
}

analyzer_t::~analyzer_t()
{
delete trace_iter;
delete trace_end;
}

bool
analyzer_t::operator!()
{
return !success;
}

bool
analyzer_t::start_reading()
{
if (!trace_iter->init()) {
ERROR("failed to read from %s\n", op_infile.get_value().empty() ?
op_ipc_name.get_value().c_str() : op_infile.get_value().c_str());
return false;
}
return true;
}
19 changes: 13 additions & 6 deletions clients/drcachesim/analyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,27 @@
#ifndef _ANALYZER_H_
#define _ANALYZER_H_ 1

#include "reader/ipc_reader.h"
#include "reader/reader.h"

class analyzer_t
{
public:
analyzer_t() {}
virtual bool init() = 0;
virtual ~analyzer_t() {};
// Usage: errors encountered during the constructor will set a flag that should
// be queried via operator!.
analyzer_t();
virtual ~analyzer_t();
virtual bool operator!();
virtual bool run() = 0;
virtual bool print_stats() = 0;

protected:
ipc_reader_t ipc_end;
ipc_reader_t ipc_iter;
// This finalizes the trace_iter setup. It can block and is meant to be
// called at the top of run().
bool start_reading();

bool success;
reader_t *trace_iter;
reader_t *trace_end;
};

#endif /* _ANALYZER_H_ */
5 changes: 5 additions & 0 deletions clients/drcachesim/common/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ droption_t<std::string> op_outdir
"For the offline analysis mode (when -offline is requested), specifies the path "
"to a directory where per-thread trace files will be written.");

droption_t<std::string> op_infile
(DROPTION_SCOPE_ALL, "infile", "", "Offline trace file for input to the simulator",
"After a trace file is produced via -offline into -outdir, it can be passed to the "
"simulator via this flag.");

droption_t<unsigned int> op_num_cores
(DROPTION_SCOPE_FRONTEND, "cores", 4, "Number of cores",
"Specifies the number of cores to simulate.");
Expand Down
1 change: 1 addition & 0 deletions clients/drcachesim/common/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
extern droption_t<bool> op_offline;
extern droption_t<std::string> op_ipc_name;
extern droption_t<std::string> op_outdir;
extern droption_t<std::string> op_infile;
extern droption_t<unsigned int> op_num_cores;
extern droption_t<unsigned int> op_line_size;
extern droption_t<bytesize_t> op_L1I_size;
Expand Down
148 changes: 79 additions & 69 deletions clients/drcachesim/launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ int
_tmain(int argc, const TCHAR *targv[])
{
char **argv;
char *app_name;
char *app_name = NULL;
char full_app_name[MAXIMUM_PATH];
char **app_argv;
char **app_argv = NULL;
int app_idx = 1;
int errcode = 1;
void *inject_data;
Expand All @@ -165,7 +165,7 @@ _tmain(int argc, const TCHAR *targv[])
analyzer_t *analyzer = NULL;
std::string tracer_ops;
#ifdef UNIX
pid_t child;
pid_t child = 0;
#endif

#if defined(WINDOWS) && !defined(_UNICODE)
Expand Down Expand Up @@ -197,42 +197,44 @@ _tmain(int argc, const TCHAR *targv[])
}
}

if (app_idx >= argc) {
FATAL_ERROR("Usage error: no application specified\nUsage:\n%s",
droption_parser_t::usage_short(DROPTION_SCOPE_ALL).c_str());
assert(false); // won't get here
}
app_name = argv[app_idx];
get_full_path(app_name, full_app_name, BUFFER_SIZE_ELEMENTS(full_app_name));
if (full_app_name[0] != '\0')
app_name = full_app_name;
NOTIFY(1, "INFO", "targeting application: \"%s\"", app_name);
if (!file_is_readable(full_app_name)) {
FATAL_ERROR("cannot find application %s", full_app_name);
assert(false); // won't get here
}
if (op_infile.get_value().empty()) {
if (app_idx >= argc) {
FATAL_ERROR("Usage error: no application specified\nUsage:\n%s",
droption_parser_t::usage_short(DROPTION_SCOPE_ALL).c_str());
assert(false); // won't get here
}
app_name = argv[app_idx];
get_full_path(app_name, full_app_name, BUFFER_SIZE_ELEMENTS(full_app_name));
if (full_app_name[0] != '\0')
app_name = full_app_name;
NOTIFY(1, "INFO", "targeting application: \"%s\"", app_name);
if (!file_is_readable(full_app_name)) {
FATAL_ERROR("cannot find application %s", full_app_name);
assert(false); // won't get here
}

if (drfront_is_64bit_app(app_name, &is64, &is32) == DRFRONT_SUCCESS &&
IF_X64_ELSE(!is64, is64 && !is32)) {
/* FIXME i#1703: since drinjectlib doesn't support cross-arch
* injection (DRi#803), we need to launch the other frontend.
*/
FATAL_ERROR("application has bitwidth unsupported by this launcher");
assert(false); // won't get here
}
if (drfront_is_64bit_app(app_name, &is64, &is32) == DRFRONT_SUCCESS &&
IF_X64_ELSE(!is64, is64 && !is32)) {
/* FIXME i#1703: since drinjectlib doesn't support cross-arch
* injection (DRi#803), we need to launch the other frontend.
*/
FATAL_ERROR("application has bitwidth unsupported by this launcher");
assert(false); // won't get here
}

app_argv = &argv[app_idx];
app_argv = &argv[app_idx];

if (!file_is_readable(op_tracer.get_value().c_str())) {
FATAL_ERROR("tracer library %s is unreadable", op_tracer.get_value().c_str());
assert(false); // won't get here
}
if (!file_is_readable(op_dr_root.get_value().c_str())) {
FATAL_ERROR("invalid -dr_root %s", op_dr_root.get_value().c_str());
assert(false); // won't get here
if (!file_is_readable(op_tracer.get_value().c_str())) {
FATAL_ERROR("tracer library %s is unreadable", op_tracer.get_value().c_str());
assert(false); // won't get here
}
if (!file_is_readable(op_dr_root.get_value().c_str())) {
FATAL_ERROR("invalid -dr_root %s", op_dr_root.get_value().c_str());
assert(false); // won't get here
}
}

if (op_offline.get_value()) {
if (op_offline.get_value() && op_infile.get_value().empty()) {
// Initial sanity check: may still be unwritable by this user, but this
// serves as at least an existence check.
if (!file_is_writable(op_outdir.get_value().c_str())) {
Expand All @@ -251,69 +253,77 @@ _tmain(int argc, const TCHAR *targv[])
return false;
}

if (!analyzer->init()) {
if (!analyzer) {
FATAL_ERROR("failed to initialize analyzer");
assert(false); // won't get here
}
}

tracer_ops = op_tracer_ops.get_value();

/* i#1638: fall back to temp dirs if there's no HOME/USERPROFILE set */
dr_get_config_dir(false/*local*/, true/*use temp*/, buf, BUFFER_SIZE_ELEMENTS(buf));
NOTIFY(1, "INFO", "DynamoRIO configuration directory is %s", buf);
if (op_infile.get_value().empty()) {
/* i#1638: fall back to temp dirs if there's no HOME/USERPROFILE set */
dr_get_config_dir(false/*local*/, true/*use temp*/, buf,
BUFFER_SIZE_ELEMENTS(buf));
NOTIFY(1, "INFO", "DynamoRIO configuration directory is %s", buf);

#ifdef UNIX
if (op_offline.get_value())
child = 0;
else
child = fork();
if (child < 0) {
FATAL_ERROR("failed to fork");
assert(false); // won't get here
} else if (child == 0) {
/* child, or offline where we exec this process */
if (op_offline.get_value())
child = 0;
else
child = fork();
if (child < 0) {
FATAL_ERROR("failed to fork");
assert(false); // won't get here
} else if (child == 0) {
/* child, or offline where we exec this process */
if (!configure_application(app_name, app_argv, tracer_ops, &inject_data) ||
!dr_inject_process_inject(inject_data, false/*!force*/, NULL)) {
FATAL_ERROR("unable to inject");
assert(false); // won't get here
}
FATAL_ERROR("failed to exec application");
}
/* parent */
#else
if (!configure_application(app_name, app_argv, tracer_ops, &inject_data) ||
!dr_inject_process_inject(inject_data, false/*!force*/, NULL)) {
FATAL_ERROR("unable to inject");
assert(false); // won't get here
}
FATAL_ERROR("failed to exec application");
}
/* parent */
#else
if (!configure_application(app_name, app_argv, tracer_ops, &inject_data) ||
!dr_inject_process_inject(inject_data, false/*!force*/, NULL)) {
FATAL_ERROR("unable to inject");
assert(false); // won't get here
}
dr_inject_process_run(inject_data);
dr_inject_process_run(inject_data);
#endif
}

if (!op_offline.get_value()) {
if (!op_offline.get_value() || !op_infile.get_value().empty()) {
if (!analyzer->run()) {
FATAL_ERROR("failed to run analyzer");
assert(false); // won't get here
}
}

if (op_infile.get_value().empty()) {
#ifdef WINDOWS
NOTIFY(1, "INFO", "waiting for app to exit...");
errcode = WaitForSingleObject(dr_inject_get_process_handle(inject_data), INFINITE);
if (errcode != WAIT_OBJECT_0)
NOTIFY(1, "INFO", "failed to wait for app: %d\n", errcode);
errcode = dr_inject_process_exit(inject_data, false/*don't kill process*/);
NOTIFY(1, "INFO", "waiting for app to exit...");
errcode = WaitForSingleObject(dr_inject_get_process_handle(inject_data),
INFINITE);
if (errcode != WAIT_OBJECT_0)
NOTIFY(1, "INFO", "failed to wait for app: %d\n", errcode);
errcode = dr_inject_process_exit(inject_data, false/*don't kill process*/);
#else
# ifndef NDEBUG
pid_t result =
pid_t result =
# endif
waitpid(child, &errcode, 0);
assert(result == child);
waitpid(child, &errcode, 0);
assert(result == child);
#endif

// XXX: we may want a prefix on our output
std::cerr << "---- <application exited with code " << errcode <<
"> ----" << std::endl;
// XXX: we may want a prefix on our output
std::cerr << "---- <application exited with code " << errcode <<
"> ----" << std::endl;
} else
errcode = 0;

analyzer->print_stats();

// release analyzer's space
Expand Down
Loading

0 comments on commit 357da25

Please sign in to comment.