From af3a792748cdbb47fa73b3ecc9b22c2a8169c1c4 Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Sun, 1 Mar 2020 18:45:56 -0500 Subject: [PATCH] i#2266: Dump memory layout to json file on annotation (#2267) Adds a new annotation DRMEMORY_ANNOTATE_DUMP_MEMORY_LAYOUT() which triggers dumping the memory layout to a new file "memlayout.nnnn.log" in the log subdirectory, where "nnnn" is incremented on each successive dump. The file is in JSON format. Exports annotation support by building the required .c file with custom flags as a library, and exports it and the two required header files drmemory_annotations.h and dr_annotations_asm.h. A basic block whose tag matches the address of the symbol "main" in the executable is watched for. Heap allocations prior to that point are marked with a new flag MALLOC_BEFORE_MAIN. The stack pointer at that point is recorded. At dump time, a heap iteration is done, skipping MALLOC_BEFORE_MAIN objects. Each object is placed into a tree. Each thread stack is also placed into a trace; fof the primary thread, only the stack region between the recorded main stack point and the TOS is considered. Both trees are then walked, with each memory region walked in pointer-sized chunks, printing out values and whether values look like pointers to the top or middle of other objects in either tree. This feature and its annotation are disabled if asm-goto is not supported by the compiler (clang<9.0), determined by try-compile. Adds a test "memlayout". Parameterizes runtest.cmake to allow checking memlayout*.json instead of results.txt. CMake is having trouble with square brackets (it always did, but there are many of them in json files), so runtest.cmake now replaces them with angle as a workaround. If jsonlint-php is found, runs it on each generated json file in the test to ensure there are no syntax errors. We install jsonlint on Travis. Documentation will be added later once post-processing is ironed out. Issue: #2266 --- .travis.yml | 12 +- CMakeLists.txt | 117 ++++++++++----- common/alloc.h | 7 +- common/alloc_replace.c | 4 +- drmemory/alloc_drmem.c | 6 +- drmemory/alloc_drmem.h | 2 +- drmemory/annotations.c | 20 ++- drmemory/annotations_public.c | 24 ++++ drmemory/annotations_public.h | 45 ++++++ drmemory/instru.c | 5 +- drmemory/leak.c | 1 + drmemory/leak.h | 2 +- drmemory/memlayout.c | 258 ++++++++++++++++++++++++++++++++++ drmemory/memlayout.h | 37 +++++ tests/CMakeLists.txt | 35 ++++- tests/memlayout.cpp | 50 +++++++ tests/memlayout.out | 35 +++++ tests/memlayout.res | 40 ++++++ tests/runsuite_wrapper.pl | 32 +++-- tests/runtest.cmake | 59 +++++--- 20 files changed, 713 insertions(+), 78 deletions(-) create mode 100644 drmemory/annotations_public.c create mode 100644 drmemory/annotations_public.h create mode 100644 drmemory/memlayout.c create mode 100644 drmemory/memlayout.h create mode 100644 tests/memlayout.cpp create mode 100644 tests/memlayout.out create mode 100644 tests/memlayout.res diff --git a/.travis.yml b/.travis.yml index 95e2b221e..5fd1dafdc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,6 +58,16 @@ jobs: - if: type != cron AND env(TRAVIS_EVENT_TYPE) != cron os: linux compiler: clang + # We need clang 9.0 for asm goto support (DRi#1799) for annotations. + env: CC=clang-9 CXX=clang++-9 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + packages: + - clang-9 - if: type != cron AND env(TRAVIS_EVENT_TYPE) != cron os: osx compiler: clang @@ -78,7 +88,7 @@ install: # XXX: Remove the "brew update" step once Travis fixes their Mac VM's # on 11/15/17. Xref https://github.com/travis-ci/travis-ci/issues/8552. - if [[ "`uname`" == "Darwin" ]]; then brew update; brew install nasm; fi - - if [[ "`uname`" == "Linux" ]]; then sudo apt-get -y install g++-multilib doxygen; fi + - if [[ "`uname`" == "Linux" ]]; then sudo apt-get -y install g++-multilib doxygen jsonlint; fi script: - tests/runsuite_wrapper.pl travis diff --git a/CMakeLists.txt b/CMakeLists.txt index 5237ef1fb..42f2537f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1220,6 +1220,7 @@ else (TOOL_DR_HEAPSTAT) drmemory/report.c drmemory/replace.c drmemory/leak.c + drmemory/memlayout.c drmemory/perturb.c common/utils.c common/utils_shared.c @@ -1688,6 +1689,70 @@ else (WIN32) endif (WIN32) copy_file_to_device("${defsupp_out}") +########################################################################### +# Dr. Memory Framework (DRMF) setup needed for tests + +set(DRMF_BASEDIR "drmf") +set(DRMF_BINBASE "${LIB_ARCH}/${build_type}") +set(DRMF_INSTALL "${INSTALL_PREFIX}${DRMF_BASEDIR}") +set(DRMF_INSTALL_BIN "${DRMF_INSTALL}/${DRMF_BINBASE}") +set(DRMF_INSTALL_INC "${DRMF_INSTALL}/include") +set(framework_dir "${PROJECT_BINARY_DIR}/${DRMF_BASEDIR}") +set(framework_incdir "${framework_dir}/include") +set(framework_bindir "${framework_dir}/${DRMF_BINBASE}") + +# Versioning: we use separate versioning for DRMF from the tool versioning. +# This is major*100 + minor. +set(DRMF_VERSION_DEFAULT "1.0.${VERSION_NUMBER_PATCHLEVEL}") +set(DRMF_VERSION "" CACHE STRING "DRMF version number: leave empty for default") +if ("${DRMF_VERSION}" STREQUAL "") + set(DRMF_VERSION ${DRMF_VERSION_DEFAULT}) +endif() +message(STATUS "DRMF version number: ${DRMF_VERSION}") +string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" DRMF_VERSION_MAJOR "${DRMF_VERSION}") +string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\..*" "\\1" DRMF_VERSION_MINOR "${DRMF_VERSION}") +# We use just major.minor for .so versioning (and no, SOVERSION is not sufficient +# here: we have to set VERSION to control the file name and import dependence). +string(REGEX REPLACE "^([0-9]+\\.[0-9]+)\\..*" "\\1" + DRMF_VERSION_MAJOR_MINOR "${DRMF_VERSION}") +math(EXPR DRMF_VERSION_INTEGER "${DRMF_VERSION_MAJOR}*100 + ${DRMF_VERSION_MINOR}") +set(DRMF_VERSION_COMPAT 9) # Oldest compatible version +set(DRMF_VERSION_CUR ${DRMF_VERSION_INTEGER}) # Current version + +configure_file(framework/public.h + ${framework_incdir}/drmemory_framework.h) +configure_file(drmemory/annotations_public.h + ${framework_incdir}/drmemory_annotations.h) +configure_file(${DynamoRIO_DIR}/../include/annotations/dr_annotations_asm.h + ${framework_incdir}/dr_annotations_asm.h) +configure_file(${DynamoRIO_DIR}/../include/annotations/dr_annotations.h + ${framework_incdir}/dr_annotations.h) +include_directories(${framework_incdir}) + +# Annotation support using DR's infrastructure (non-Valgrind-style). +if (UNIX) + set(annot_cflags "-O0 -Wno-unused-variable -Wno-return-type") +else () + set(annot_cflags "/Od /Ob0 /GL- /wd4715") +endif () +if (UNIX) + # DRi#1799: clang prior to 9.0 does not support "asm goto" which is used by + # the DR annotation infrastructure. But, we can't just do a version check + # because Apple's clang has a completely separate version number. + try_compile(DR_ANNOTATIONS_SUPPORTED ${PROJECT_BINARY_DIR}/try_annot + SOURCES ${PROJECT_SOURCE_DIR}/tests/memlayout.cpp + ${PROJECT_SOURCE_DIR}/drmemory/annotations_public.c CMAKE_FLAGS + -DINCLUDE_DIRECTORIES=${framework_incdir} + -DCMAKE_C_FLAGS:STRING="${annot_cflags}") + if (DR_ANNOTATIONS_SUPPORTED) + message(STATUS "DR annotations are supported") + else () + message(STATUS "DR annotations are NOT supported: probably this is clang<9.0") + endif () +else () + set(DR_ANNOTATIONS_SUPPORTED ON) +endif () + ########################################################################### # Tests @@ -1766,37 +1831,6 @@ endif () # framework-shared code goes here. Things that should be global we # put into functions for invocation inside extension subdirs. -set(DRMF_BASEDIR "drmf") -set(DRMF_BINBASE "${LIB_ARCH}/${build_type}") -set(DRMF_INSTALL "${INSTALL_PREFIX}${DRMF_BASEDIR}") -set(DRMF_INSTALL_BIN "${DRMF_INSTALL}/${DRMF_BINBASE}") -set(DRMF_INSTALL_INC "${DRMF_INSTALL}/include") -set(framework_dir "${PROJECT_BINARY_DIR}/${DRMF_BASEDIR}") -set(framework_incdir "${framework_dir}/include") -set(framework_bindir "${framework_dir}/${DRMF_BINBASE}") - -# Versioning: we use separate versioning for DRMF from the tool versioning. -# This is major*100 + minor. -set(DRMF_VERSION_DEFAULT "1.0.${VERSION_NUMBER_PATCHLEVEL}") -set(DRMF_VERSION "" CACHE STRING "DRMF version number: leave empty for default") -if ("${DRMF_VERSION}" STREQUAL "") - set(DRMF_VERSION ${DRMF_VERSION_DEFAULT}) -endif() -message(STATUS "DRMF version number: ${DRMF_VERSION}") -string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" DRMF_VERSION_MAJOR "${DRMF_VERSION}") -string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\..*" "\\1" DRMF_VERSION_MINOR "${DRMF_VERSION}") -# We use just major.minor for .so versioning (and no, SOVERSION is not sufficient -# here: we have to set VERSION to control the file name and import dependence). -string(REGEX REPLACE "^([0-9]+\\.[0-9]+)\\..*" "\\1" - DRMF_VERSION_MAJOR_MINOR "${DRMF_VERSION}") -math(EXPR DRMF_VERSION_INTEGER "${DRMF_VERSION_MAJOR}*100 + ${DRMF_VERSION_MINOR}") -set(DRMF_VERSION_COMPAT 9) # Oldest compatible version -set(DRMF_VERSION_CUR ${DRMF_VERSION_INTEGER}) # Current version - -configure_file(framework/public.h - ${framework_incdir}/drmemory_framework.h) -include_directories(${framework_incdir}) - configure_file(framework/drmf.cmake.in ${framework_dir}/DrMemoryFrameworkConfig.cmake @ONLY) @@ -1842,6 +1876,20 @@ SET_PROPERTY(TARGET ${ARGV0} PROPERTY MAP_IMPORTED_CONFIG_RELMINSIZE RelWithDebI set(exported_targets_append "${exported_targets_append}" PARENT_SCOPE) endmacro(export_target) +if (DR_ANNOTATIONS_SUPPORTED) + add_library(drmemory_annotations STATIC drmemory/annotations_public.c) + # TODO i#2266: Provide a CMake function like use_DynamoRIO_annotations. + # We would both use it here and export it. + # For now we make a library, which is actually a little easier than the + # user having to compile our source file with special flags. + # So the CMake function would just set up the include path and link to the lib. + _DR_append_property_string(SOURCE drmemory/annotations_public.c + COMPILE_FLAGS "${annot_cflags}") + export_target(drmemory_annotations) + install(TARGETS drmemory_annotations EXPORT ${exported_targets_name} + DESTINATION ${DRMF_INSTALL_BIN}) +endif () + # Included prior to add_subdirectory() for building version.c which # includes utils.h which includes drsyscall.h. include_directories(drsyscall) @@ -2188,7 +2236,12 @@ else (BUILDING_SUB_PACKAGE) endif (BUILDING_SUB_PACKAGE) # DRMF install rules -install(FILES ${framework_incdir}/drmemory_framework.h DESTINATION ${DRMF_INSTALL_INC}) +install(FILES + ${framework_incdir}/drmemory_framework.h + ${framework_incdir}/drmemory_annotations.h + ${framework_incdir}/dr_annotations.h + ${framework_incdir}/dr_annotations_asm.h + DESTINATION ${DRMF_INSTALL_INC}) install(FILES ${framework_dir}/DrMemoryFrameworkConfigVersion.cmake ${framework_dir}/DrMemoryFrameworkConfig.cmake DESTINATION ${DRMF_INSTALL}) diff --git a/common/alloc.h b/common/alloc.h index 42aedd38d..96eb8d7db 100644 --- a/common/alloc.h +++ b/common/alloc.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2010-2017 Google, Inc. All rights reserved. + * Copyright (c) 2010-2020 Google, Inc. All rights reserved. * Copyright (c) 2008-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -118,8 +118,11 @@ enum { MALLOC_RESERVED_7 = 0x0400, MALLOC_RESERVED_8 = 0x0800, MALLOC_RESERVED_9 = 0x1000, + MALLOC_RESERVED_10= 0x2000, + MALLOC_CLIENT_5 = 0x4000, MALLOC_POSSIBLE_CLIENT_FLAGS = (MALLOC_CLIENT_1 | MALLOC_CLIENT_2 | - MALLOC_CLIENT_3 | MALLOC_CLIENT_4), + MALLOC_CLIENT_3 | MALLOC_CLIENT_4 | + MALLOC_CLIENT_5), }; /* Info on a malloc chunk used for malloc iteration and client notification */ diff --git a/common/alloc_replace.c b/common/alloc_replace.c index c0aa82fe5..cf5d7225d 100644 --- a/common/alloc_replace.c +++ b/common/alloc_replace.c @@ -146,8 +146,8 @@ enum { /* i#1532: only check for non-static libc. This is Windows-only but it's * cleaner to avoid all the ifdefs down below. */ - CHUNK_LAYER_NOCHECK = 0x1000, - CHUNK_SKIP_ITER = 0x2000, + CHUNK_LAYER_NOCHECK = MALLOC_RESERVED_9, + CHUNK_SKIP_ITER = MALLOC_RESERVED_10, /* meta-flags */ #ifdef WINDOWS diff --git a/drmemory/alloc_drmem.c b/drmemory/alloc_drmem.c index 53f681870..b02869d97 100644 --- a/drmemory/alloc_drmem.c +++ b/drmemory/alloc_drmem.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2010-2019 Google, Inc. All rights reserved. + * Copyright (c) 2010-2020 Google, Inc. All rights reserved. * Copyright (c) 2008-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -31,6 +31,7 @@ #include "heap.h" #include "redblack.h" #include "leak.h" +#include "memlayout.h" #include "alloc_drmem.h" #ifdef UNIX # ifdef MACOS @@ -232,6 +233,8 @@ alloc_drmem_init(void) end_of_defined_region, is_register_defined); + memlayout_init(); + if (options.delay_frees > 0) { delay_free_lock = dr_mutex_create(); delay_free_tree = rb_tree_create(NULL); @@ -549,6 +552,7 @@ client_handle_malloc(void *drcontext, malloc_info_t *mal, dr_mcontext_t *mc) report_malloc(mal->base, mal->base + mal->request_size, mal->realloc ? "realloc" : "malloc", mc); leak_handle_alloc(drcontext, mal->base, mal->request_size); + memlayout_handle_alloc(drcontext, mal->base, mal->request_size); } void diff --git a/drmemory/alloc_drmem.h b/drmemory/alloc_drmem.h index de9c1e07a..316e65425 100644 --- a/drmemory/alloc_drmem.h +++ b/drmemory/alloc_drmem.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2013-2017 Google, Inc. All rights reserved. + * Copyright (c) 2013-2020 Google, Inc. All rights reserved. * Copyright (c) 2008-2009 VMware, Inc. All rights reserved. * **********************************************************/ diff --git a/drmemory/annotations.c b/drmemory/annotations.c index 17ac10257..6b6f5947a 100644 --- a/drmemory/annotations.c +++ b/drmemory/annotations.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2015 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * **********************************************************/ /* Dr. Memory: the memory debugger @@ -40,6 +40,7 @@ #include "options.h" #ifdef TOOL_DR_MEMORY # include "alloc_drmem.h" +# include "memlayout.h" #else extern void check_reachability(bool at_exit); #endif @@ -74,6 +75,13 @@ handle_do_leak_check(dr_vg_client_request_t *request) check_reachability(false/*!at_exit*/); return 0; } + +void handle_dump_memory_layout(void) +{ +# ifdef TOOL_DR_MEMORY + memlayout_dump_layout(); +# endif +} #endif void @@ -88,6 +96,16 @@ annotate_init(void) handle_do_leak_check); # endif #endif + +#ifndef ARM /* FIXME DRi#1672: add ARM annotation support to DR */ + /* TODO i#2266: We want to pass the PC to the handler. */ + if (!dr_annotation_register_call("drmemory_dump_memory_layout", + handle_dump_memory_layout, false, 0, + DR_ANNOTATION_CALL_TYPE_FASTCALL)) { + NOTIFY_ERROR("ERROR: Failed to register annotations"NL); + dr_abort(); + } +#endif } void diff --git a/drmemory/annotations_public.c b/drmemory/annotations_public.c new file mode 100644 index 000000000..f1a1383d3 --- /dev/null +++ b/drmemory/annotations_public.c @@ -0,0 +1,24 @@ +/* ********************************************************** + * Copyright (c) 2020 Google, Inc. All rights reserved. + * **********************************************************/ + +/* Dr. Memory: the memory debugger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License, and no later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "dr_annotations.h" + +DR_DEFINE_ANNOTATION(void, drmemory_dump_memory_layout, (void),) diff --git a/drmemory/annotations_public.h b/drmemory/annotations_public.h new file mode 100644 index 000000000..d82c2336c --- /dev/null +++ b/drmemory/annotations_public.h @@ -0,0 +1,45 @@ +/* ********************************************************** + * Copyright (c) 2020 Google, Inc. All rights reserved. + * **********************************************************/ + +/* Dr. Memory: the memory debugger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License, and no later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _ANNOTATIONS_PUBLIC_H_ +#define _ANNOTATIONS_PUBLIC_H_ 1 + +#include "dr_annotations_asm.h" + +/* To simplify project configuration, this pragma excludes the file from GCC warnings. */ +#ifdef __GNUC__ +# pragma GCC system_header +#endif + +#define DRMEMORY_ANNOTATE_DUMP_MEMORY_LAYOUT() \ + DR_ANNOTATION(drmemory_dump_memory_layout) + +#ifdef __cplusplus +extern "C" { +#endif + +DR_DECLARE_ANNOTATION(void, drmemory_dump_memory_layout, (void)); + +#ifdef __cplusplus +} +#endif + +#endif /* _ANNOTATIONS_PUBLIC_H_ */ diff --git a/drmemory/instru.c b/drmemory/instru.c index 5c7540bf0..91c46ccb7 100644 --- a/drmemory/instru.c +++ b/drmemory/instru.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2010-2017 Google, Inc. All rights reserved. + * Copyright (c) 2010-2020 Google, Inc. All rights reserved. * Copyright (c) 2008-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -29,6 +29,7 @@ #include "drutil.h" #include "drmemory.h" #include "slowpath.h" +#include "memlayout.h" #include "spill.h" #include "fastpath.h" #include "stack.h" @@ -1002,6 +1003,8 @@ instru_event_bb_analysis(void *drcontext, void *tag, instrlist_t *bb, LOG(4, "ilist before analysis:\n"); DOLOG(4, instrlist_disassemble(drcontext, tag, bb, LOGFILE_GET(drcontext));); + memlayout_handle_new_block(drcontext, tag); + #ifdef USE_DRSYMS /* symbol of each bb is very useful for debugging */ DOLOG(3, { diff --git a/drmemory/leak.c b/drmemory/leak.c index 7c9a011a7..5004b0a8c 100644 --- a/drmemory/leak.c +++ b/drmemory/leak.c @@ -45,6 +45,7 @@ enum { MALLOC_MAYBE_REACHABLE = MALLOC_CLIENT_3, /* Indirect leak (PR 576032) */ MALLOC_INDIRECTLY_REACHABLE = MALLOC_CLIENT_4, + MALLOC_BEFORE_MAIN = MALLOC_CLIENT_5, }; /* the lowest possbile pointer value */ diff --git a/drmemory/leak.h b/drmemory/leak.h index 53db0a248..802934235 100644 --- a/drmemory/leak.h +++ b/drmemory/leak.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2012-2014 Google, Inc. All rights reserved. + * Copyright (c) 2012-2020 Google, Inc. All rights reserved. * Copyright (c) 2008-2010 VMware, Inc. All rights reserved. * **********************************************************/ diff --git a/drmemory/memlayout.c b/drmemory/memlayout.c new file mode 100644 index 000000000..f41b6da88 --- /dev/null +++ b/drmemory/memlayout.c @@ -0,0 +1,258 @@ +/* ********************************************************** + * Copyright (c) 2020 Google, Inc. All rights reserved. + * **********************************************************/ + +/* Dr. Memory: the memory debugger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License, and no later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "dr_api.h" +#include "drx.h" +#include "drmemory.h" +#include "utils.h" +#include "memlayout.h" +#include "alloc.h" +#include "heap.h" +#include "redblack.h" +#ifdef TOOL_DR_MEMORY +# include "shadow.h" +#endif + +static app_pc app_main_addr; +static bool reached_main; /* Assumed atomic enough to write to it. */ +byte *xsp_at_main; + +/* We claim the 5th malloc client flag */ +enum { + MALLOC_BEFORE_MAIN = MALLOC_CLIENT_5, +}; + +typedef struct _layout_data_t { + file_t outf; + /* Tree for lookup and iteration of the heap. */ + rb_tree_t *heap_tree; + /* Tree for lookup and iteration of the valid stack regions. */ + rb_tree_t *stack_tree; + /* Used to distinguish in memory_layout_rb_iter. */ + bool walking_heap; + /* Used to prevent a trailing JSON comma. */ + bool entry_count; +} layout_data_t; + +void +memlayout_init(void) +{ + module_data_t *exe = dr_get_main_module(); + app_main_addr = lookup_symbol(exe, "main"); + if (app_main_addr == NULL) { + NOTIFY_ERROR("ERROR: Failed to find \"main\" for limiting memory dump"NL); + reached_main = true; /* just dump everything */ + } + LOG(1, "main is at " PFX "\n", app_main_addr); + dr_free_module_data(exe); +} + +void +memlayout_handle_new_block(void *drcontext, void *tag) +{ + if (!reached_main && dr_fragment_app_pc(tag) == app_main_addr) { + reached_main = true; + LOG(1, "reached main\n"); + dr_mcontext_t mc; /* do not init whole thing: memset is expensive */ + mc.size = sizeof(mc); + mc.flags = DR_MC_CONTROL; + dr_get_mcontext(drcontext, &mc); + xsp_at_main = (byte *)mc.xsp; + } +} + +/* User must call from client_handle_malloc() and client_handle_realloc() */ +void +memlayout_handle_alloc(void *drcontext, app_pc base, size_t size) +{ + if (!reached_main) + malloc_set_client_flag(base, MALLOC_BEFORE_MAIN); +} + +static bool +memory_layout_malloc_iter(malloc_info_t *info, void *iter_data) +{ + layout_data_t *data = (layout_data_t *)iter_data; + if (info->pre_us || TEST(MALLOC_BEFORE_MAIN, info->client_flags)) + return true; + rb_insert(data->heap_tree, info->base, info->request_size, NULL); + return true; +} + +static void +memory_layout_walk_chunk(layout_data_t *data, byte *base, size_t size) +{ + for (byte *addr = base; addr < base + size; ) { + /* We assume it's safe to deref these selected regions, and + * to de-ref off the end of any non-aligned object. + */ + if (addr > base) + ELOGF(0, data->outf, ",\n"); + ELOGF(0, data->outf, " {\n"); + ELOGF(0, data->outf, " \"address\": \"" PFX "\",\n", addr); + size_t sz = base + size - addr; + if (sz >= sizeof(void*)) { + byte *value = *(byte**)addr; + /* No trailing commas on final item! */ + ELOGF(0, data->outf, " \"value\": \"" PFX "\"", value); + addr += sizeof(void*); + rb_node_t *target = rb_in_node(data->heap_tree, value); + bool tgt_stack = false; + if (target == NULL) { + target = rb_in_node(data->stack_tree, value); + tgt_stack = true; + } + if (target != NULL) { + byte *tgt_base; + rb_node_fields(target, &tgt_base, NULL, NULL); + ELOGF(0, data->outf, ",\n \"points-to-type\": \"%s\",\n", + tgt_stack ? "stack" : "heap"); + ELOGF(0, data->outf, " \"points-to-base\": \"" PFX "\",\n", + tgt_base); + ELOGF(0, data->outf, " \"points-to-offset\": \"0x%zx\"", + value - tgt_base); + } + ELOGF(0, data->outf, "\n"); + } else if (sz >= sizeof(int)) { + ELOGF(0, data->outf, " \"value\": \"0x%08x\"\n", *(int*)addr); + addr += sizeof(int); + } else if (sz >= sizeof(short)) { + ELOGF(0, data->outf, " \"value\": \"0x%04x\"\n", (short)*(int*)addr); + addr += sizeof(short); + } else { + ELOGF(0, data->outf, " \"value\": \"0x%02x\"\n", (char)*(int*)addr); + addr += sizeof(char); + } + ELOGF(0, data->outf, " }"); + } + ELOGF(0, data->outf, "\n"); +} + +static bool +memory_layout_rb_iter(rb_node_t *node, void *iter_data) +{ + layout_data_t *data = (layout_data_t *)iter_data; + byte *base; + size_t size; + app_pc pc; + rb_node_fields(node, &base, &size, (void**)&pc); + if (data->entry_count++ > 0) + ELOGF(0, data->outf, ",\n"); + if (data->walking_heap) { + ELOGF(0, data->outf, " {\n \"address\": \"" PFX "\",\n", base); + ELOGF(0, data->outf, " \"size\": \"%d\",\n", size); + } else { + /* TODO DRi#4146: Add mechanism to get actual PC. */ + ELOGF(0, data->outf, + " {\n \"thread_pc (CURRENTLY_BROKEN)\": \"" PFX "\",\n", pc); + ELOGF(0, data->outf, " \"address\": \"" PFX "\",\n", base); + ELOGF(0, data->outf, " \"size\": \"%d\",\n", size); + } + ELOGF(0, data->outf, " \"contents\": [\n", size); + memory_layout_walk_chunk(data, base, size); + ELOGF(0, data->outf, " ]\n"); + ELOGF(0, data->outf, " }"); + return true; +} + +static void +memory_layout_record_stack_region(void *drcontext, layout_data_t *data) +{ + dr_mcontext_t mc; /* do not init whole thing: memset is expensive */ + mc.size = sizeof(mc); + mc.flags = DR_MC_CONTROL; + dr_get_mcontext(drcontext, &mc); + byte *stack_res_base; + size_t stack_sz = allocation_size((byte *)mc.xsp, &stack_res_base); + size_t record_sz = (size_t)(stack_res_base + stack_sz - mc.xsp); + if (xsp_at_main > stack_res_base && xsp_at_main < stack_res_base + stack_sz) + record_sz = xsp_at_main - (byte*)mc.xsp; + else { + /* TODO i#2266: Record the high-level thread-func xsp point and use here. */ + } + /* TODO DRi#4146: Add mechanism to get PC passed to annotation handler. + * Right now mc.pc is 0 since we're in a clean call and didn't pass it in. + */ + rb_insert(data->stack_tree, (byte*)mc.xsp, record_sz, (void*)mc.pc); +} + +void +memlayout_dump_layout(void) +{ + char fname[MAXIMUM_PATH]; + file_t outf = drx_open_unique_file(logsubdir, + "memlayout", "json", +#ifndef WINDOWS + DR_FILE_CLOSE_ON_FORK | +#endif + DR_FILE_ALLOW_LARGE, + fname, BUFFER_SIZE_ELEMENTS(fname)); + if (outf == INVALID_FILE) { + NOTIFY_ERROR("Failed to open layout output file"NL); + dr_abort(); + } + NOTIFY("Memory layout written to: %s" NL, fname); + LOG(1, "Memory layout written to: %s\n", fname); + + layout_data_t data; + memset(&data, 0, sizeof(data)); + data.outf = outf; + data.heap_tree = rb_tree_create(NULL); + data.stack_tree = rb_tree_create(NULL); + + void **drcontexts = NULL; + uint num_threads = 0; + if (!dr_suspend_all_other_threads(&drcontexts, &num_threads, NULL)) { + NOTIFY_ERROR("Failed to suspend threads for memory layout dump"NL); + dr_abort(); + } + + malloc_iterate(memory_layout_malloc_iter, &data); + + for (uint i = 0; i < num_threads; i++) { + memory_layout_record_stack_region(drcontexts[i], &data); + } + memory_layout_record_stack_region(dr_get_current_drcontext(), &data); + + ELOGF(0, data.outf, "{\n \"heap objects\": [\n"); + data.walking_heap = true; + data.entry_count = 0; + rb_iterate(data.heap_tree, memory_layout_rb_iter, &data); + if (data.entry_count > 0) + ELOGF(0, data.outf, "\n"); + ELOGF(0, data.outf, " ],\n \"thread stacks\": [\n"); + data.walking_heap = false; + data.entry_count = 0; + rb_iterate(data.stack_tree, memory_layout_rb_iter, &data); + if (data.entry_count > 0) + ELOGF(0, data.outf, "\n"); + ELOGF(0, data.outf, " ]\n}\n"); + + if (drcontexts != NULL) { + IF_DEBUG(bool ok =) + dr_resume_all_other_threads(drcontexts, num_threads); + ASSERT(ok, "failed to resume after leak scan"); + } + + rb_tree_destroy(data.heap_tree); + rb_tree_destroy(data.stack_tree); + dr_close_file(outf); +} diff --git a/drmemory/memlayout.h b/drmemory/memlayout.h new file mode 100644 index 000000000..c1725cc44 --- /dev/null +++ b/drmemory/memlayout.h @@ -0,0 +1,37 @@ +/* ********************************************************** + * Copyright (c) 2020 Google, Inc. All rights reserved. + * **********************************************************/ + +/* Dr. Memory: the memory debugger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License, and no later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MEMLAYOUT_H_ +#define _MEMLAYOUT_H_ 1 + +void +memlayout_init(void); + +void +memlayout_handle_new_block(void *drcontext, void *tag); + +void +memlayout_handle_alloc(void *drcontext, app_pc base, size_t size); + +void +memlayout_dump_layout(void); + +#endif /* _MEMLAYOUT_H_ */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ecc7619d2..d3a994a69 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ # ********************************************************** -# Copyright (c) 2010-2019 Google, Inc. All rights reserved. +# Copyright (c) 2010-2020 Google, Inc. All rights reserved. # Copyright (c) 2009-2010 VMware, Inc. All rights reserved. # ********************************************************** @@ -122,7 +122,7 @@ function(set_props target) endfunction(set_props) function(tobuild name source) - set(srcs ${source}) + set(srcs ${source} ${ARGN}) # support same-file asm code if (NOT DEFINED ${name}_disable_asm) @@ -328,9 +328,11 @@ function(newtest_nobuild_allparams test exe test_ops drmem_ops dr_ops postproces if (postprocess) # test -skip_results + -results (PR 575481) set(postcmd "${cmd_base};-results") - else (postprocess) + elseif (DEFINED ${test}.postcmd) + set(postcmd ${${test}.postcmd}) + else () set(postcmd "") - endif (postprocess) + endif () endif (TOOL_DR_HEAPSTAT) string(REGEX REPLACE " " "@@" postcmd "${postcmd}") string(REGEX REPLACE ";" "@" postcmd "${postcmd}") @@ -343,12 +345,20 @@ function(newtest_nobuild_allparams test exe test_ops drmem_ops dr_ops postproces else (X64) set(TOOL_BIN "bin32") endif (X64) + if (postprocess) + set(resmark "To obtain results, run with: -results") + elseif (DEFINED ${test}.resmark) + set(resmark ${${test}.resmark}) + else () + set(resmark "Details:") + endif () convert_local_path_to_device_path(dr_device_path ${DynamoRIO_DIR}) add_test(${test} ${CMAKE_COMMAND} -D cmd:STRING=${cmd} -D TOOL_DR_HEAPSTAT:BOOL=${TOOL_DR_HEAPSTAT} -D outpat:STRING=${src_param_pattern}/${resbase}.out -D respat:STRING=${src_param_pattern}/${resbase}.res + -D resmark:STRING=${resmark} -D nudge:STRING=${nudge} -D VMKERNEL:BOOL=${VMKERNEL} -D USE_DRSYMS:BOOL=${USE_DRSYMS} @@ -910,6 +920,23 @@ if (TOOL_DR_MEMORY) if (NOT ARM) # XXX i#1726: port to ARM newtest_nobuild_ex(state.pattern state "" "-unaddr_only" "" OFF "" "ANY" "") endif () + + # XXX DRi#1672: add ARM annotation support to DR + if (DR_ANNOTATIONS_SUPPORTED AND NOT ARM) + set(memlayout.resmark "Memory layout written to:") + find_program(JSONLINT jsonlint-php DOC "JSON linter from jsonlint package") + if (JSONLINT) + set(memlayout.postcmd "${JSONLINT}") + message(STATUS "Found ${JSONLINT}: will use to validate json output") + endif () + tobuild(memlayout memlayout.cpp) + target_link_libraries(memlayout drmemory_annotations) + newtest_nobuild_ex(memlayout memlayout "" "" "" OFF "" 0 "") + # We want a simple stack layout. + append_test_compile_flags(memlayout "-O0") + target_include_directories(memlayout PRIVATE ${framework_incdir}) + endif () + else (TOOL_DR_MEMORY) newtest_ex(stale stale.c "" "-staleness;-stale_granularity;100" "" OFF "" 0) diff --git a/tests/memlayout.cpp b/tests/memlayout.cpp new file mode 100644 index 000000000..3feb157d1 --- /dev/null +++ b/tests/memlayout.cpp @@ -0,0 +1,50 @@ +/* ********************************************************** + * Copyright (c) 2020 Google, Inc. All rights reserved. + * **********************************************************/ + +/* Dr. Memory: the memory debugger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License, and no later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "drmemory_annotations.h" +#include + +int main() +{ + int i,**j,k,l,*m; + i = 0; + j = new int*[3]; + j[0] = new int; + j[1] = &i; + m = *(j+1); + DRMEMORY_ANNOTATE_DUMP_MEMORY_LAYOUT(); + j[1] = &k; + k=10; + *(j[0]) = 5; + j[2] = j[0]; + *(j[0]) = 18; + *m = 4; + l = 3; + + char *ch = new char[13]; + ch[4] = 'x'; + + DRMEMORY_ANNOTATE_DUMP_MEMORY_LAYOUT(); + + std::cerr << "goodbye\n"; + + return 0; +} diff --git a/tests/memlayout.out b/tests/memlayout.out new file mode 100644 index 000000000..f33143a08 --- /dev/null +++ b/tests/memlayout.out @@ -0,0 +1,35 @@ +# ********************************************************** +# Copyright (c) 2020 Google, Inc. All rights reserved. +# ********************************************************** +# +# Dr. Memory: the memory debugger +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; +# version 2.1 of the License, and no later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +~~Dr.M~~ Memory layout written to: +~~Dr.M~~ Memory layout written to: +goodbye +~~Dr.M~~ ERRORS FOUND: +~~Dr.M~~ 0 unique, 0 total unaddressable access(es) +~~Dr.M~~ 0 unique, 0 total uninitialized access(es) +~~Dr.M~~ 0 unique, 0 total invalid heap argument(s) +~~Dr.M~~ 0 unique, 0 total warning(s) +%if X32 +~~Dr.M~~ 2 unique, 2 total, 29 byte(s) of leak(s) +%endif +%if X64 +~~Dr.M~~ 2 unique, 2 total, 41 byte(s) of leak(s) +%endif +~~Dr.M~~ 0 unique, 0 total, 0 byte(s) of possible leak(s) diff --git a/tests/memlayout.res b/tests/memlayout.res new file mode 100644 index 000000000..c5c9bb720 --- /dev/null +++ b/tests/memlayout.res @@ -0,0 +1,40 @@ +# ********************************************************** +# Copyright (c) 2020 Google, Inc. All rights reserved. +# ********************************************************** +# +# Dr. Memory: the memory debugger +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; +# version 2.1 of the License, and no later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +{ + "heap objects": [ + { + "address": "", +%if X32 + "size": "12", +%endif +%if X64 + "size": "24", +%endif + "contents": [ + { + "address": "", + "value": "", + "points-to-type": "heap", + "points-to-base": "", + "points-to-offset": "" + }, +# We just make sure we have a stack section. + "thread stacks": [ diff --git a/tests/runsuite_wrapper.pl b/tests/runsuite_wrapper.pl index b1a593ff9..95eac3075 100755 --- a/tests/runsuite_wrapper.pl +++ b/tests/runsuite_wrapper.pl @@ -61,15 +61,29 @@ # we can diagnose failures. # We tee to stdout to provide incremental output and avoid the 10-min # no-output timeout on Travis. +print "Forking child for stdout tee\n"; my $res = ''; my $child = open(CHILD, '-|'); die "Failed to fork: $!" if (!defined($child)); if ($child) { # Parent - my $output; - while () { - print STDOUT $_; - $res .= $_; + # i#4126: We include extra printing to help diagnose hangs on Travis. + if ($^O ne 'cygwin') { + print "Parent tee-ing child stdout...\n"; + local $SIG{ALRM} = sub { + print "\nxxxxxxxxxx 30s elapsed xxxxxxxxxxx\n"; + alarm(30); + }; + alarm(30); + while () { + print STDOUT $_; + $res .= $_; + } + } else { + while () { + print STDOUT $_; + $res .= $_; + } } close(CHILD); } elsif ($ENV{'TRAVIS_EVENT_TYPE'} eq 'cron' || @@ -105,11 +119,11 @@ system("${cmd} 2>&1"); exit 0; } else { - # To shrink the log sizes and make Travis and Appveyor error pages easier - # to work with we omit a second V and instead use --output-on-failure. - # We rely on runsuite_common_post.cmake extracting configure and build error - # details from the xml files, as they don't show up with one V. - system("ctest --output-on-failure -V -S \"${osdir}/runsuite.cmake${args}\" 2>&1"); + # Despite creating larger log files, -VV makes it easier to diagnose issues. + my $cmd = "ctest --output-on-failure -VV -S \"${osdir}/runsuite.cmake${args}\""; + print "Running ${cmd}\n"; + system("${cmd} 2>&1"); + print "Finished running ${cmd}\n"; exit 0; } diff --git a/tests/runtest.cmake b/tests/runtest.cmake index fa1b0b45f..4a429ee88 100755 --- a/tests/runtest.cmake +++ b/tests/runtest.cmake @@ -1,5 +1,5 @@ # ********************************************************** -# Copyright (c) 2010-2019 Google, Inc. All rights reserved. +# Copyright (c) 2010-2020 Google, Inc. All rights reserved. # Copyright (c) 2009-2010 VMware, Inc. All rights reserved. # ********************************************************** @@ -23,7 +23,8 @@ # * cmd = command to run, with intra-arg space=@@ and inter-arg space=@ # * TOOL_DR_HEAPSTAT = whether the tool is Dr. Heapstat instead of Dr. Memory # * outpat = file containing expected patterns in output -# * respat = file containing expected patterns in results.txt +# * respat = file containing expected patterns in resfile +# * resmark = prefix in output to file path(s) to compare ${respat} to # * nudge = command to run perl script that takes -nudge for nudge # * toolbindir = location of DynamoRIO tools dir # * VMKERNEL = whether running on vmkernel @@ -45,9 +46,9 @@ # * DRMEMORY_CTEST_SRC_DIR = source dir # * DRMEMORY_CTEST_DR_DIR = DynamoRIO cmake dir # -# any regex chars in the patterns will be escaped. -# a line beginning with # is a comment and is ignored. -# basic conditionals are "%if WINDOWS" and "%if UNIX" ending with +# Any regex chars in the patterns will be escaped. +# A line beginning with # is a comment and is ignored. +# Basic conditionals are "%if WINDOWS" and "%if UNIX" ending with # "%endif". ################################################## @@ -337,13 +338,20 @@ endif () # process the patterns foreach (str ${patterns}) - # turn regex chars into literals - string(REGEX REPLACE "([\\^\\$\\.\\*\\+\\?\\|\\(\\)\\[])" "\\\\\\1" ${str} "${${str}}") - # \\] somehow messes up the match when inside the long string so we separate it - string(REGEX REPLACE "\\]" "\\\\]" ${str} "${${str}}") - + # Square brackets cause a lot of problem. In the .res file, they cause failures + # to treat as a list when we split on newlines: + # 55: ******* line is | "heap objects": \[ + # 55: ; { + # 55: ; "address": "0x900007f655e313fb0",| + # We replace them with angle brackets here and in the actual output below + # as a workaround. + string(REPLACE "]" ">" ${str} "${${str}}") + string(REPLACE "[" "<" ${str} "${${str}}") + # Turn regex chars into literals. + string(REGEX REPLACE "([\\^\\$\\.\\*\\+\\?\\|\\(\\)\\[])" "\\\\\\1" + ${str} "${${str}}") # remove comments - string(REGEX REPLACE "(^|\n)#[^\n]*\n" "\\1\n" ${str} "${${str}}") + string(REGEX REPLACE "(^|\n)#[^\n]*\n" "\\1" ${str} "${${str}}") # evaluate conditionals # cmake's regex matcher is maximal unfortunately: for now we disallow % @@ -473,6 +481,9 @@ endif (WIN32) # remove trailing spaces string(REGEX REPLACE " *\n" "\n" cmd_tomatch "${cmd_tomatch}") +# See above: CMake has bugs handling square brackets. +string(REPLACE "]" ">" cmd_tomatch "${cmd_tomatch}") +string(REPLACE "[" "<" cmd_tomatch "${cmd_tomatch}") foreach (line ${lines}) set(remove_line ON) @@ -517,7 +528,7 @@ foreach (line ${lines}) endforeach (line) ################################################## -# check results.txt +# check results file # XXX i#1688: Disable leak tests for Dr. Heapstat until the offline # processor is refactored. if (resmatch AND NOT TOOL_DR_HEAPSTAT) @@ -525,21 +536,16 @@ if (resmatch AND NOT TOOL_DR_HEAPSTAT) string(REGEX REPLACE "@@" " " postcmd "${postcmd}") string(REGEX REPLACE "@" ";" postcmd "${postcmd}") endif (NOT "${postcmd}" STREQUAL "") - if ("${postcmd}" STREQUAL "") - set(data_prefix "Details: ") - else () - set(data_prefix "To obtain results, run with: -results ") - endif () # it may not be created yet set(iters 0) - while (NOT "${cmd_err}" MATCHES "${data_prefix}") + while (NOT "${cmd_err}" MATCHES "${resmark}") execute_process(COMMAND ${SLEEP_SHORT}) math(EXPR iters "${iters} + 1") if ("${iters}" STREQUAL "${TIMEOUT_SHORT}") message(FATAL_ERROR "Timed out waiting for Dr. Memory to finish") endif () endwhile () - string(REGEX MATCHALL "${data_prefix}([^\n]+)[\n]" resfiles "${cmd_err}") + string(REGEX MATCHALL "${resmark}([^\n]+)[\n]" resfiles "${cmd_err}") set(maxlen 0) foreach (resfile ${resfiles}) @@ -547,12 +553,13 @@ if (resmatch AND NOT TOOL_DR_HEAPSTAT) # available on vmkernel (grrr...) so we take the largest (can't rely # on last being the right one, and exec target malloc will produce # larger log than parent or pre-exec child) - string(REGEX REPLACE "${data_prefix}" "" resfile "${resfile}") + string(REGEX REPLACE "${resmark} *" "" resfile "${resfile}") string(REGEX REPLACE "[\n]" "" resfile "${resfile}") if (NOT "${postcmd}" STREQUAL "") # generate resfile set(thiscmd "${postcmd};${resfile}") + message("Running ${thiscmd}") execute_process(COMMAND ${thiscmd} RESULT_VARIABLE postcmd_result ERROR_VARIABLE postcmd_err @@ -561,7 +568,9 @@ if (resmatch AND NOT TOOL_DR_HEAPSTAT) message(FATAL_ERROR "*** ${thiscmd} failed (${postcmd_result}): ${postcmd_err}***\n") endif (postcmd_result) - set(resfile "${resfile}/results.txt") + if (${postcmd} MATCHES "-results") + set(resfile "${resfile}/results.txt") + endif () else (NOT "${postcmd}" STREQUAL "") set(postcmd_err "") endif (NOT "${postcmd}" STREQUAL "") @@ -592,8 +601,8 @@ if (resmatch AND NOT TOOL_DR_HEAPSTAT) endforeach (resfile) # remove absolute addresses (from PR 535568) - string(REGEX REPLACE " 0x[0-9a-f]+-0x[0-9a-f]+" "" results "${results}") - string(REGEX REPLACE " 0x[0-9a-f]+" "" results "${results}") + string(REGEX REPLACE " *0x[0-9a-f]+-0x[0-9a-f]+" "" results "${results}") + string(REGEX REPLACE " *0x[0-9a-f]+" "" results "${results}") # canonicalize by removing ".exe" (XXX: maybe should have regex in .res instead?) string(REGEX REPLACE "\\.exe!" "!" results "${results}") # canonicalize asm file name, which varies by VS vs ninja vs gcc @@ -603,6 +612,10 @@ if (resmatch AND NOT TOOL_DR_HEAPSTAT) string(REGEX REPLACE "c_asm\\.asm[\\.a-z]*" "c_asm.asm" results "${results}") string(REGEX REPLACE "cpp_asm\\.asm[\\.a-z]*" "cpp_asm.asm" results "${results}") + # See above: CMake has bugs handling square brackets. + string(REPLACE "]" ">" results "${results}") + string(REPLACE "[" "<" results "${results}") + string(REGEX MATCHALL "([^\n]+)\n" lines "${resmatch}") set(require_in_order 1) set(empty_ok 0)