blob: c31cfe6d8c13d876e2a12b4d14c435ff1f1e6e96 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2021-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.
*/
/* Illustrates using the drcallstack extension.
*
* The drcallstack extension only supports Linux in this release.
* This sample wraps malloc and every time it's called it symbolizes and
* prints the callstack.
*/
#include "dr_api.h"
#include "drmgr.h"
#include "drwrap.h"
#include "drcallstack.h"
#include "drsyms.h"
#include "droption.h"
#include <string>
namespace dynamorio {
namespace samples {
namespace {
using ::dynamorio::droption::DROPTION_SCOPE_CLIENT;
using ::dynamorio::droption::droption_t;
static droption_t<std::string> trace_function(
DROPTION_SCOPE_CLIENT, "trace_function", "malloc", "Name of function to trace",
"The name of the function to wrap and print callstacks on every call.");
static void
print_qualified_function_name(app_pc pc)
{
module_data_t *mod = dr_lookup_module(pc);
if (mod == NULL) {
// If we end up in assembly code or generated code we'll likely never
// get out again without stack scanning or frame pointer walking or
// other strategies not yet part of drcallstack.
dr_fprintf(STDERR, " <unknown module> @%p\n", pc);
return;
}
drsym_info_t sym_info;
#define MAX_FUNC_LEN 1024
char name[MAX_FUNC_LEN];
char file[MAXIMUM_PATH];
sym_info.struct_size = sizeof(sym_info);
sym_info.name = name;
sym_info.name_size = MAX_FUNC_LEN;
sym_info.file = file;
sym_info.file_size = MAXIMUM_PATH;
const char *func = "<unknown>";
drsym_error_t sym_res =
drsym_lookup_address(mod->full_path, pc - mod->start, &sym_info, DRSYM_DEMANGLE);
if (sym_res == DRSYM_SUCCESS)
func = sym_info.name;
dr_fprintf(STDERR, " %s!%s\n", dr_module_preferred_name(mod), func);
dr_free_module_data(mod);
}
static void
wrap_pre(void *wrapcxt, DR_PARAM_OUT void **user_data)
{
dr_fprintf(STDERR, "%s called from:\n", trace_function.get_value().c_str());
// Get the context. The pc field is set by drwrap to the wrapped function
// entry point.
dr_mcontext_t *mc = drwrap_get_mcontext(wrapcxt);
// Walk the callstack.
drcallstack_walk_t *walk;
drcallstack_status_t res = drcallstack_init_walk(mc, &walk);
DR_ASSERT(res == DRCALLSTACK_SUCCESS);
drcallstack_frame_t frame = {
sizeof(frame),
};
int count = 0;
print_qualified_function_name(drwrap_get_func(wrapcxt));
do {
res = drcallstack_next_frame(walk, &frame);
if (res != DRCALLSTACK_SUCCESS)
break;
print_qualified_function_name(frame.pc);
++count;
} while (res == DRCALLSTACK_SUCCESS);
// The return value DRCALLSTACK_NO_MORE_FRAMES indicates a complete callstack.
// Anything else indicates some kind of unwind info error.
// If this code were used inside a larger tool it would be up to that tool
// whether to record or act on the callstack quality.
res = drcallstack_cleanup_walk(walk);
DR_ASSERT(res == DRCALLSTACK_SUCCESS);
}
static void
module_load_event(void *drcontext, const module_data_t *mod, bool loaded)
{
size_t modoffs;
drsym_error_t sym_res = drsym_lookup_symbol(
mod->full_path, trace_function.get_value().c_str(), &modoffs, DRSYM_DEMANGLE);
if (sym_res == DRSYM_SUCCESS) {
app_pc towrap = mod->start + modoffs;
bool ok = drwrap_wrap(towrap, wrap_pre, NULL);
DR_ASSERT(ok);
dr_fprintf(STDERR, "wrapping %s!%s\n", mod->full_path,
trace_function.get_value().c_str());
}
}
static void
module_unload_event(void *drcontext, const module_data_t *mod)
{
size_t modoffs;
drsym_error_t sym_res = drsym_lookup_symbol(
mod->full_path, trace_function.get_value().c_str(), &modoffs, DRSYM_DEMANGLE);
if (sym_res == DRSYM_SUCCESS) {
app_pc towrap = mod->start + modoffs;
bool ok = drwrap_unwrap(towrap, wrap_pre, NULL);
DR_ASSERT(ok);
}
}
static void
event_exit()
{
drmgr_register_module_unload_event(module_unload_event);
drcallstack_exit();
drwrap_exit();
drsym_exit();
}
} // namespace
} // namespace samples
} // namespace dynamorio
DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[])
{
dr_set_client_name("DynamoRIO Sample Client 'callstack'",
"http://dynamorio.org/issues");
// Parse our option.
if (!dynamorio::droption::droption_parser_t::parse_argv(
dynamorio::droption::DROPTION_SCOPE_CLIENT, argc, argv, NULL, NULL))
DR_ASSERT(false);
drcallstack_options_t ops = {
sizeof(ops),
};
// Initialize the libraries we're using.
if (!drwrap_init() || drcallstack_init(&ops) != DRCALLSTACK_SUCCESS ||
drsym_init(0) != DRSYM_SUCCESS ||
!drmgr_register_module_load_event(dynamorio::samples::module_load_event))
DR_ASSERT(false);
dr_register_exit_event(dynamorio::samples::event_exit);
// Improve performance as we only need basic wrapping support.
drwrap_set_global_flags(
static_cast<drwrap_global_flags_t>(DRWRAP_NO_FRILLS | DRWRAP_FAST_CLEANCALLS));
}