Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i#2414 drcallstack: Add libunwind-based callstack walking #5154

Merged
merged 17 commits into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c16ccbc
i#2414 drcallstack: Add libunwind-based callstack walking
derekbruening Oct 4, 2021
73621cc
Check for libuwind.h before enabling drcallstack; make that required …
derekbruening Oct 11, 2021
9849862
Install 32-bit libunwind-dev for x86-32 job; fix mac unused-func warn…
derekbruening Oct 11, 2021
ec1a5a4
Install libunwind-dev:i386 for 32-bit x86; Mac build fix
derekbruening Oct 11, 2021
d6d9b72
Remove unw_init_local2 use since build may be run on another machine
derekbruening Oct 12, 2021
91bccf8
Reviewer suggestions: mostly comment changes; memcpy for r0..rN
derekbruening Oct 12, 2021
6e5d402
i#5153 packaging: add cmake support for DESTDIR install relocation (#…
fmoessbauer Oct 12, 2021
ef1c483
Add different repository for libunwind-dev:i386
derekbruening Oct 12, 2021
b31be0c
Obtain aarchxx libunwind packages from native versions and copy the f…
derekbruening Oct 12, 2021
5207ef1
Merge branch 'master' of github.com:DynamoRIO/dynamorio into i2414-li…
derekbruening Oct 12, 2021
ff5c757
Specify all deps of libunwind-dev:i386; fix arm64 package spec error
derekbruening Oct 12, 2021
4869a5a
Try again to deal w/ weird dep issues; use ../extract to avoid vera++…
derekbruening Oct 12, 2021
7ebfa01
Try manual install of i386 packages
derekbruening Oct 12, 2021
7b2a928
Remove assert for DRCALLSTACK_NO_MORE_FRAMES as we get a vague error …
derekbruening Oct 12, 2021
1f5e71c
Add comment to sample about DRCALLSTACK_NO_MORE_FRAMES indicating a c…
derekbruening Oct 13, 2021
e198bbe
Re-require libunwind for cross-compile TEST_SUITE
derekbruening Oct 13, 2021
fa29d0e
Do not require libunwind for Android nor a64-on-x86
derekbruening Oct 13, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions .github/workflows/ci-aarchxx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,20 @@ jobs:

- run: git fetch --no-tags --depth=1 origin master

# Install cross-compiler for cross-compiling Linux build:
# Install cross-compiler for cross-compiling Linux build.
# Unfortunately there is no libunwind-dev or libunwind cross-compile
# package so we unpack the native versions and copy their files.
- name: Create Build Environment
run: |
sudo apt-get update
sudo apt-get -y install doxygen vera++ cmake zlib1g-dev libsnappy-dev \
g++-aarch64-linux-gnu qemu-user qemu-user-binfmt
sudo add-apt-repository 'deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports focal main'
apt download libunwind8:arm64 libunwind-dev:arm64 liblzma5:arm64
mkdir ../extract
for i in *.deb; do dpkg-deb -x $i ../extract; done
for i in include lib; do sudo rsync -av ../extract/usr/${i}/aarch64-linux-gnu/ /usr/aarch64-linux-gnu/${i}/; done
sudo rsync -av ../extract/lib/aarch64-linux-gnu/ /usr/aarch64-linux-gnu/lib/

- name: Run Suite
working-directory: ${{ github.workspace }}
Expand Down Expand Up @@ -119,12 +127,20 @@ jobs:

- run: git fetch --no-tags --depth=1 origin master

# Install cross-compiler for cross-compiling Linux build:
# Install cross-compiler for cross-compiling Linux build.
# Unfortunately there is no libunwind-dev or libunwind cross-compile
# package so we unpack the native versions and copy their files.
- name: Create Build Environment
run: |
sudo apt-get update
sudo apt-get -y install doxygen vera++ cmake zlib1g-dev libsnappy-dev \
g++-arm-linux-gnueabihf qemu-user qemu-user-binfmt
sudo add-apt-repository 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports focal main'
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
apt download libunwind8:armhf libunwind-dev:armhf liblzma5:armhf
mkdir ../extract
for i in *.deb; do dpkg-deb -x $i ../extract; done
for i in include lib; do sudo rsync -av ../extract/usr/${i}/arm-linux-gnueabihf/ /usr/arm-linux-gnueabihf/${i}/; done
derekbruening marked this conversation as resolved.
Show resolved Hide resolved
sudo rsync -av ../extract/lib/arm-linux-gnueabihf/ /usr/arm-linux-gnueabihf/lib/

- name: Run Suite
working-directory: ${{ github.workspace }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-clang-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
- name: Create Build Environment
run: |
sudo apt-get -y install doxygen vera++ zlib1g-dev libsnappy-dev \
clang-format-6.0
clang-format-6.0 libunwind-dev

- name: Run Suite
working-directory: ${{ github.workspace }}
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/ci-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,22 @@ jobs:
git clone --depth=2 https://github.com/DynamoRIO/drmemory.git drmemory
cd drmemory && git submodule update --init --depth 250 && cd ..

# Install multilib for non-cross-compiling Linux build:
# Install multilib for non-cross-compiling Linux build.
# GA CI uses packages.microsoft.com which is missing i386 packages, and
# attempts at using apt with us.archive-ubuntu.com hit dep issues:
# so we manually install the i386 packages we need.
- name: Create Build Environment
run: |
sudo apt update
sudo apt-get -y install doxygen vera++ zlib1g-dev libsnappy-dev \
g++-multilib
g++-multilib libunwind-dev
sudo add-apt-repository 'deb [arch=i386] http://us.archive.ubuntu.com/ubuntu focal main'
apt download libunwind8:i386 libunwind-dev:i386 liblzma5:i386
mkdir ../extract
for i in *.deb; do dpkg-deb -x $i ../extract; done
sudo rsync -av ../extract/usr/lib/i386-linux-gnu/ /usr/lib/i386-linux-gnu/
sudo rsync -av ../extract/lib/i386-linux-gnu/ /usr/lib/i386-linux-gnu/
sudo rsync -av ../extract/usr/include/i386-linux-gnu/ /usr/include/

# Use a newer cmake to avoid 32-bit toolchain problems (i#4830).
- name: Setup newer cmake
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/ci-x86.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,21 @@ jobs:
- run: git fetch --no-tags --depth=1 origin master

# Install multilib for non-cross-compiling Linux build.
# GA CI uses packages.microsoft.com which is missing i386 packages, and
# attempts at using apt with us.archive-ubuntu.com hit dep issues:
# so we manually install the i386 packages we need.
- name: Create Build Environment
run: |
sudo apt update
sudo apt-get -y install doxygen vera++ zlib1g-dev libsnappy-dev \
g++-multilib
g++-multilib libunwind-dev
sudo add-apt-repository 'deb [arch=i386] http://us.archive.ubuntu.com/ubuntu focal main'
apt download libunwind8:i386 libunwind-dev:i386 liblzma5:i386
mkdir ../extract
for i in *.deb; do dpkg-deb -x $i ../extract; done
sudo rsync -av ../extract/usr/lib/i386-linux-gnu/ /usr/lib/i386-linux-gnu/
sudo rsync -av ../extract/lib/i386-linux-gnu/ /usr/lib/i386-linux-gnu/
sudo rsync -av ../extract/usr/include/i386-linux-gnu/ /usr/include/

# Use a newer cmake to avoid 32-bit toolchain problems (i#4830).
- name: Setup newer cmake
Expand Down Expand Up @@ -141,7 +151,7 @@ jobs:
run: |
sudo apt update
sudo apt-get -y install doxygen vera++ zlib1g-dev libsnappy-dev \
g++-multilib
g++-multilib libunwind-dev

# Use a newer cmake to avoid 32-bit toolchain problems (i#4830).
- name: Setup newer cmake
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1178,8 +1178,10 @@ endif (NOT DISABLE_WARNINGS)

if (LINUX)
CHECK_INCLUDE_FILE("linux/rseq.h" HAVE_RSEQ)
CHECK_INCLUDE_FILE("libunwind.h" HAVE_LIBUNWIND_H)
else ()
set(HAVE_RSEQ OFF)
set(HAVE_LIBUNWIND_H OFF)
endif ()

# Currently only AArch64 targets supported for half-precision FP.
Expand Down
4 changes: 2 additions & 2 deletions api/docs/building.dox
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ To build DynamoRIO on Linux, use the following commands as a guide. This builds
```
# Install dependencies for Ubuntu 15+. Adjust this command as appropriate for
# other distributions (in particular, use "cmake3" for Ubuntu Trusty).
$ sudo apt-get install cmake g++ g++-multilib doxygen git zlib1g-dev
$ sudo apt-get install cmake g++ g++-multilib doxygen git zlib1g-dev libunwind-dev
# Get sources.
$ git clone https://github.com/DynamoRIO/dynamorio.git
# Make a separate build directory. Building in the source directory is not
Expand Down Expand Up @@ -165,7 +165,7 @@ In order to build both 32-bit and 64-bit on the same platform you'll need
compiler support for targeting both architectures. Then install these packages:

```
sudo apt-get install cmake g++ g++-multilib doxygen
sudo apt-get install cmake g++ g++-multilib doxygen zlib1g-dev libunwind-dev libunwind-dev:i386
```

For older distributions, the `ia32-libs` package is needed.
Expand Down
3 changes: 3 additions & 0 deletions api/docs/release.dox
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ Further non-compatibility-affecting changes include:
and exhaustive machine state comparisons across instrumentation sequences.
- Added drmodtrack_lookup_segment().
- Added a new drrun option \p -attach for attaching to a running process.
This is currently an experimental option.
- Added \ref page_drcallstack Extension for walking application callstacks, with
an initial Linux-only implementation.

**************************************************
<hr>
Expand Down
2 changes: 2 additions & 0 deletions api/docs/samples.dox
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ for reporting the dynamic execution count of all basic blocks.
The sample <a href="https://github.com/DynamoRIO/dynamorio/tree/master/api/samples/bbsize.c">bbsize.c</a>
collects statistics on the sizes of all basic blocks in the target application.

The sample <a href="https://github.com/DynamoRIO/dynamorio/tree/master/api/samples/callstack.c">callstack.c</a> walks the application callstack at a given function entry point.

The sample <a href="https://github.com/DynamoRIO/dynamorio/tree/master/api/samples/cbr.c">cbr.c</a> collects conditional branch
execution information and shows how to dynamically
update or replace instrumented code after it executes.
Expand Down
8 changes: 7 additions & 1 deletion api/samples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# **********************************************************
# Copyright (c) 2010-2020 Google, Inc. All rights reserved.
# Copyright (c) 2010-2021 Google, Inc. All rights reserved.
# Copyright (c) 2009-2010 VMware, Inc. All rights reserved.
# **********************************************************

Expand Down Expand Up @@ -227,6 +227,12 @@ configure_DynamoRIO_global(OFF ON)
add_sample_client(bbbuf "bbbuf.c" "drmgr;drreg;drx")
add_sample_client(bbcount "bbcount.c" "drmgr;drreg;drx")
add_sample_client(bbsize "bbsize.c" "drmgr;drx")
if (LINUX)
CHECK_INCLUDE_FILE("libunwind.h" HAVE_LIBUNWIND_H)
if (HAVE_LIBUNWIND_H)
add_sample_client(callstack "callstack.cpp" "drmgr;drwrap;drcallstack;drsyms;droption")
endif ()
endif ()
add_sample_client(div "div.c" "drmgr")
add_sample_client(empty "empty.c" "")
add_sample_client(memtrace_simple "memtrace_simple.c;utils.c" "drmgr;drreg;drutil;drx")
Expand Down
173 changes: 173 additions & 0 deletions api/samples/callstack.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/* **********************************************************
* Copyright (c) 2021 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 {

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, 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

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 (!droption_parser_t::parse_argv(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(module_load_event))
DR_ASSERT(false);
dr_register_exit_event(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));
}
Loading