From a4f3761de2decbdb5b74505ce712d2e717e8a685 Mon Sep 17 00:00:00 2001 From: Andrew Bonventre Date: Tue, 12 Jan 2016 15:23:09 -0500 Subject: [PATCH 01/16] Switch to Gerrit for code review Change-Id: I36baf766e17cf7f86d14fd28f8e6c22008c8f6f4 --- codereview.settings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codereview.settings b/codereview.settings index c3673e8d..a553cffb 100644 --- a/codereview.settings +++ b/codereview.settings @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -CODE_REVIEW_SERVER: codereview.chromium.org +GERRIT_HOST: True +CODE_REVIEW_SERVER: https://canary-chromium-review.googlesource.com/ CC_LIST: crashpad-dev@chromium.org VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/ PROJECT: crashpad From 2e448328500f596814545e64bb6f43211476c955 Mon Sep 17 00:00:00 2001 From: Andrew Bonventre Date: Thu, 14 Jan 2016 10:28:58 -0500 Subject: [PATCH 02/16] Remove CC_LIST from codereview.settings It has no effect on Gerrit hosts. The notification settings within Gerrit need to be configured. Change-Id: I6ab9ed0d6f55742ebe934924cc9272efd7e2f3c6 BUG= Change-Id: I592e0dcacfc41d90bf7c0e6800cd8e2c411c6c2c Reviewed-on: https://chromium-review.googlesource.com/322190 Tested-by: Andrew Bonventre Reviewed-by: Mark Mentovai --- codereview.settings | 1 - 1 file changed, 1 deletion(-) diff --git a/codereview.settings b/codereview.settings index a553cffb..38784224 100644 --- a/codereview.settings +++ b/codereview.settings @@ -14,6 +14,5 @@ GERRIT_HOST: True CODE_REVIEW_SERVER: https://canary-chromium-review.googlesource.com/ -CC_LIST: crashpad-dev@chromium.org VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/ PROJECT: crashpad From 4794225f22583a08732cb9ad4356106013a220f3 Mon Sep 17 00:00:00 2001 From: Patrick Monette Date: Mon, 18 Jan 2016 15:05:20 -0500 Subject: [PATCH 03/16] Adding an API to read module annotations in snapshot.gyp Kasko needs a way to read crash keys from out of process. This API reuses the functionality of PEImageAnnotationsReader. Change-Id: I2f3bbc358212e6f50235183e9dbb4e5a2cf989cf This is a reupload of https://codereview.chromium.org/1586433003/ but for gerrit. Change-Id: I2f3bbc358212e6f50235183e9dbb4e5a2cf989cf Reviewed-on: https://chromium-review.googlesource.com/322550 Reviewed-by: Scott Graham Tested-by: Scott Graham Reviewed-by: Scott Graham --- snapshot/api/module_annotations_win.cc | 53 ++++++++++++ snapshot/api/module_annotations_win.h | 42 ++++++++++ snapshot/api/module_annotations_win_test.cc | 83 +++++++++++++++++++ snapshot/snapshot.gyp | 27 ++++++ snapshot/snapshot_test.gyp | 2 + .../win/pe_image_annotations_reader_test.cc | 1 - snapshot/win/pe_image_reader_test.cc | 11 +-- util/util.gyp | 2 + util/win/get_module_information.cc | 30 +++++++ util/win/get_module_information.h | 33 ++++++++ 10 files changed, 273 insertions(+), 11 deletions(-) create mode 100644 snapshot/api/module_annotations_win.cc create mode 100644 snapshot/api/module_annotations_win.h create mode 100644 snapshot/api/module_annotations_win_test.cc create mode 100644 util/win/get_module_information.cc create mode 100644 util/win/get_module_information.h diff --git a/snapshot/api/module_annotations_win.cc b/snapshot/api/module_annotations_win.cc new file mode 100644 index 00000000..8f444fa5 --- /dev/null +++ b/snapshot/api/module_annotations_win.cc @@ -0,0 +1,53 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/api/module_annotations_win.h" + +#include "snapshot/win/pe_image_annotations_reader.h" +#include "snapshot/win/pe_image_reader.h" +#include "snapshot/win/process_reader_win.h" +#include "util/win/get_module_information.h" + +namespace crashpad { + +bool ReadModuleAnnotations(HANDLE process, + HMODULE module, + std::map* annotations) { + ProcessReaderWin process_reader; + if (!process_reader.Initialize(process, ProcessSuspensionState::kRunning)) + return false; + + MODULEINFO module_info; + if (!CrashpadGetModuleInformation( + process, module, &module_info, sizeof(module_info))) { + PLOG(ERROR) << "CrashpadGetModuleInformation"; + return false; + } + + PEImageReader image_reader; + if (!image_reader.Initialize( + &process_reader, + reinterpret_cast(module_info.lpBaseOfDll), + module_info.SizeOfImage, + "")) + return false; + + PEImageAnnotationsReader annotations_reader( + &process_reader, &image_reader, L""); + + *annotations = annotations_reader.SimpleMap(); + return true; +} + +} // namespace crashpad diff --git a/snapshot/api/module_annotations_win.h b/snapshot/api/module_annotations_win.h new file mode 100644 index 00000000..e0240310 --- /dev/null +++ b/snapshot/api/module_annotations_win.h @@ -0,0 +1,42 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_API_MODULE_ANNOTATIONS_WIN_H_ +#define CRASHPAD_SNAPSHOT_API_MODULE_ANNOTATIONS_WIN_H_ + +#include + +#include +#include + +namespace crashpad { + +//! \brief Reads the module annotations from another process. +//! +//! \param[in] process The handle to the process that hosts the \a module. +//! Requires PROCESS_QUERY_INFORMATION and PROCESS_VM_READ accesses. +//! \param[in] module The handle to the module from which the \a annotations +//! will be read. This module should be loaded in the target process. +//! \param[out] annotations The map that will be filled with the annotations. +//! Remains unchanged if the function returns 'false'. +//! +//! \return `true` if the annotations could be read succesfully, even if the +//! module doesn't contain any annotations. +bool ReadModuleAnnotations(HANDLE process, + HMODULE module, + std::map* annotations); + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_API_MODULE_ANNOTATIONS_WIN_H_ diff --git a/snapshot/api/module_annotations_win_test.cc b/snapshot/api/module_annotations_win_test.cc new file mode 100644 index 00000000..30e880f3 --- /dev/null +++ b/snapshot/api/module_annotations_win_test.cc @@ -0,0 +1,83 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/api/module_annotations_win.h" + +#include "client/crashpad_info.h" +#include "gtest/gtest.h" +#include "test/win/win_multiprocess.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class ModuleAnnotationsMultiprocessTest final : public WinMultiprocess { + private: + void WinMultiprocessParent() override { + // Read the child executable module. + HMODULE module = nullptr; + CheckedReadFile(ReadPipeHandle(), &module, sizeof(module)); + + // Reopen the child process with necessary access. + HANDLE process_handle = + OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, + GetProcessId(ChildProcess())); + EXPECT_TRUE(process_handle); + + // Read the module annotations in the child process and verify them. + std::map annotations; + ASSERT_TRUE(ReadModuleAnnotations(process_handle, module, &annotations)); + + EXPECT_GE(annotations.size(), 3u); + EXPECT_EQ("value", annotations["#APITEST# key"]); + EXPECT_EQ("y", annotations["#APITEST# x"]); + EXPECT_EQ("", annotations["#APITEST# empty_value"]); + + // Signal the child process to terminate. + char c = ' '; + CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); + } + + void WinMultiprocessChild() override { + // Set some test annotations. + crashpad::CrashpadInfo* crashpad_info = + crashpad::CrashpadInfo::GetCrashpadInfo(); + + crashpad::SimpleStringDictionary* simple_annotations = + new crashpad::SimpleStringDictionary(); + simple_annotations->SetKeyValue("#APITEST# key", "value"); + simple_annotations->SetKeyValue("#APITEST# x", "y"); + simple_annotations->SetKeyValue("#APITEST# empty_value", ""); + + crashpad_info->set_simple_annotations(simple_annotations); + + // Send the executable module. + HMODULE module = GetModuleHandle(nullptr); + CheckedWriteFile(WritePipeHandle(), &module, sizeof(module)); + + // Wait until a signal from the parent process to terminate. + char c; + CheckedReadFile(ReadPipeHandle(), &c, sizeof(c)); + } +}; + +TEST(ModuleAnnotationsWin, ReadAnnotations) { + WinMultiprocess::Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 29711b06..0154495d 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -129,5 +129,32 @@ }], ] }, + { + 'variables': { + 'conditions': [ + ['OS == "win"', { + 'snapshot_api_target_type%': 'static_library', + }, { + # There are no source files except on Windows. + 'snapshot_api_target_type%': 'none', + }], + ], + }, + 'target_name': 'crashpad_snapshot_api', + 'type': '<(snapshot_api_target_type)', + 'dependencies': [ + 'crashpad_snapshot', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'api/module_annotations_win.cc', + 'api/module_annotations_win.h', + ], + }, ], } diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index 51274e6c..9bfcf8b3 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -54,6 +54,7 @@ 'dependencies': [ 'crashpad_snapshot_test_module', 'snapshot.gyp:crashpad_snapshot', + 'snapshot.gyp:crashpad_snapshot_api', '../client/client.gyp:crashpad_client', '../compat/compat.gyp:crashpad_compat', '../test/test.gyp:crashpad_test', @@ -68,6 +69,7 @@ 'sources': [ 'cpu_context_test.cc', 'crashpad_info_client_options_test.cc', + 'api/module_annotations_win_test.cc', 'mac/cpu_context_mac_test.cc', 'mac/mach_o_image_annotations_reader_test.cc', 'mac/mach_o_image_reader_test.cc', diff --git a/snapshot/win/pe_image_annotations_reader_test.cc b/snapshot/win/pe_image_annotations_reader_test.cc index 9bdf1245..523a4494 100644 --- a/snapshot/win/pe_image_annotations_reader_test.cc +++ b/snapshot/win/pe_image_annotations_reader_test.cc @@ -31,7 +31,6 @@ #include "snapshot/win/process_reader_win.h" #include "test/paths.h" #include "test/win/child_launcher.h" -#include "test/win/win_multiprocess.h" #include "util/file/file_io.h" #include "util/win/process_info.h" diff --git a/snapshot/win/pe_image_reader_test.cc b/snapshot/win/pe_image_reader_test.cc index 46795cfb..704b6591 100644 --- a/snapshot/win/pe_image_reader_test.cc +++ b/snapshot/win/pe_image_reader_test.cc @@ -22,7 +22,7 @@ #include "gtest/gtest.h" #include "snapshot/win/process_reader_win.h" #include "test/errors.h" -#include "util/win/get_function.h" +#include "util/win/get_module_information.h" #include "util/win/module_version.h" #include "util/win/process_info.h" @@ -32,15 +32,6 @@ namespace crashpad { namespace test { namespace { -BOOL CrashpadGetModuleInformation(HANDLE process, - HMODULE module, - MODULEINFO* module_info, - DWORD cb) { - static const auto get_module_information = - GET_FUNCTION_REQUIRED(L"psapi.dll", ::GetModuleInformation); - return get_module_information(process, module, module_info, cb); -} - TEST(PEImageReader, DebugDirectory) { PEImageReader pe_image_reader; ProcessReaderWin process_reader; diff --git a/util/util.gyp b/util/util.gyp index 6d0090f4..48194907 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -166,6 +166,8 @@ 'win/exception_handler_server.h', 'win/get_function.cc', 'win/get_function.h', + 'win/get_module_information.cc', + 'win/get_module_information.h', 'win/handle.cc', 'win/handle.h', 'win/module_version.cc', diff --git a/util/win/get_module_information.cc b/util/win/get_module_information.cc new file mode 100644 index 00000000..1a9fd0c4 --- /dev/null +++ b/util/win/get_module_information.cc @@ -0,0 +1,30 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/win/get_module_information.h" + +#include "util/win/get_function.h" + +namespace crashpad { + +BOOL CrashpadGetModuleInformation(HANDLE process, + HMODULE module, + MODULEINFO* module_info, + DWORD cb) { + static const auto get_module_information = + GET_FUNCTION_REQUIRED(L"psapi.dll", GetModuleInformation); + return get_module_information(process, module, module_info, cb); +} + +} // namespace crashpad diff --git a/util/win/get_module_information.h b/util/win/get_module_information.h new file mode 100644 index 00000000..c7d1cf17 --- /dev/null +++ b/util/win/get_module_information.h @@ -0,0 +1,33 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_ +#define CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_ + +#include + +#define PSAPI_VERSION 1 +#include + +namespace crashpad { + +//! \brief Proxy function for `GetModuleInformation()`. +BOOL CrashpadGetModuleInformation(HANDLE process, + HMODULE module, + MODULEINFO* module_info, + DWORD cb); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_ From 82af299823c15a6a30891317efc903642d91bffe Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Thu, 21 Jan 2016 12:35:34 -0500 Subject: [PATCH 04/16] Update mini_chromium to 7c5b0c1ab44a4264b02f4c825a5b73d9173253e4 7c5b0c1ab44a Use Gerrit (Polygerrit) for mini_chromium code review Change-Id: I883672b91dac5387031e913dd62a1802d2163e9f Reviewed-on: https://chromium-review.googlesource.com/323140 Reviewed-by: Robert Sesek --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 902915ce..829ae27c 100644 --- a/DEPS +++ b/DEPS @@ -25,7 +25,7 @@ deps = { '01528c7244837168a1c80f06ff60fa5a9793c824', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'a43fee120b10ed71df4e55a370948ca461d78232', + '7c5b0c1ab44a4264b02f4c825a5b73d9173253e4', 'buildtools': Var('chromium_git') + '/chromium/buildtools.git@' + 'c2f259809d5ede3275df5ea0842f0431990c4f98', From 88eea80ad32871e85acc8303c37fb033265c67cb Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 27 Jan 2016 11:05:20 -0500 Subject: [PATCH 05/16] =?UTF-8?q?Add=20an=20=E2=80=9Cexternal=E2=80=9D=20m?= =?UTF-8?q?ode=20for=20obtaining=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This augments the standalone and in-Chromium models with an external model, in which the dependencies and Crashpad are checked out as siblings in the same directory, organized according to this structure: root/crashpad[/README] root/gmock[/include/gmock/gmock.h] root/gtest[/include/gtest/gtest.h] root/gyp[/pylib/gyp] root/mini_chromium[/build/common.gypi] This is the directory structure used in google3. Change-Id: Ie300ead7cd085265933e4ed891509ce050e995e2 Reviewed-on: https://chromium-review.googlesource.com/324230 Reviewed-by: Robert Sesek Reviewed-by: Scott Graham --- build/crashpad_dependencies.gypi | 42 +++++ build/crashpad_in_chromium.gypi | 43 ----- build/gyp_crashpad.py | 47 +++++- handler/handler.gyp | 4 +- third_party/gtest/gmock.gyp | 107 +++++++------ third_party/gtest/gtest.gyp | 168 +++++++++++--------- third_party/mini_chromium/mini_chromium.gyp | 24 ++- 7 files changed, 255 insertions(+), 180 deletions(-) create mode 100644 build/crashpad_dependencies.gypi delete mode 100644 build/crashpad_in_chromium.gypi diff --git a/build/crashpad_dependencies.gypi b/build/crashpad_dependencies.gypi new file mode 100644 index 00000000..417d462b --- /dev/null +++ b/build/crashpad_dependencies.gypi @@ -0,0 +1,42 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +{ + # Crashpad can obtain dependencies in three different ways, directed by the + # crashpad_standalone GYP variable. It may have these values: + # standalone + # A “standalone” Crashpad build, where the dependencies are in the + # Crashpad tree. third_party/mini_chromium and third_party/gtest provide + # the base and gtest libraries. + # chromium + # An in-Chromium build, where Crashpad is within the Chromium tree. + # Chromium provides its own base library and its copy of the gtest + # library. + # external + # A build with external dependencies. mini_chromium provides the base + # library, but it’s located outside of the Crashpad tree, as is gtest. + # + # In order for Crashpad’s .gyp files to reference the correct versions + # depending on how dependencies are being provided, include this .gypi file + # and reference the crashpad_dependencies variable. + + 'variables': { + # When building as a standalone project or with external dependencies, + # build/gyp_crashpad.py sets crashpad_dependencies to "standalone" or + # "external", and this % assignment will not override it. The variable will + # not be set by anything else when building as part of Chromium, so in that + # case, this will define it with value "chromium". + 'crashpad_dependencies%': 'chromium', + }, +} diff --git a/build/crashpad_in_chromium.gypi b/build/crashpad_in_chromium.gypi deleted file mode 100644 index 0f646377..00000000 --- a/build/crashpad_in_chromium.gypi +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2015 The Crashpad Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -{ - # Crashpad can build as a standalone project or as part of Chromium. When - # building as a standalone project, it uses mini_chromium to provide the base - # library, and uses its own copy of gtest in third_party. When building as - # part of Chromium, it uses Chromium’s base library and copy of gtest. In - # order for Crashpad’s .gyp files to reference the correct versions depending - # on whether building standalone or as a part of Chromium, include this .gypi - # file and reference the crashpad_in_chromium variable. - - 'variables': { - 'variables': { - # When building as a standalone project, build/gyp_crashpad.py sets - # crashpad_standalone to 1, and this % assignment will not override it. - # The variable will not be set when building as part of Chromium, so in - # that case, this will define it with value 0. - 'crashpad_standalone%': 0, - }, - - 'conditions': [ - ['crashpad_standalone!=0', { - 'crashpad_in_chromium': 0, - }, { - 'crashpad_in_chromium': 1, - }], - ], - - 'crashpad_in_chromium': '<(crashpad_in_chromium)', - }, -} diff --git a/build/gyp_crashpad.py b/build/gyp_crashpad.py index 42818eca..7496cb1f 100755 --- a/build/gyp_crashpad.py +++ b/build/gyp_crashpad.py @@ -17,10 +17,38 @@ import os import sys + +def ChooseDependencyPath(local_path, external_path): + """Chooses between a dependency located at local path and an external path. + + The local path, used in standalone builds, is preferred. If it is not present + but the external path is, the external path will be used. If neither path is + present, the local path will be used, so that error messages uniformly refer + to the local path. + + Args: + local_path: The preferred local path to use for a standalone build. + external_path: The external path to fall back to. + + Returns: + A 2-tuple. The first element is 'standalone' or 'external', depending on + whether local_path or external_path was chosen. The second element is the + chosen path. + """ + if os.path.exists(local_path) or not os.path.exists(external_path): + return ('standalone', local_path) + return ('external', external_path) + + script_dir = os.path.dirname(__file__) -crashpad_dir = os.path.dirname(script_dir) if script_dir is not '' else '..' -sys.path.insert( - 0, os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp', 'pylib')) +crashpad_dir = (os.path.dirname(script_dir) if script_dir not in ('', os.curdir) + else os.pardir) + +sys.path.insert(0, + ChooseDependencyPath(os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp', + 'pylib'), + os.path.join(crashpad_dir, os.pardir, 'gyp', + 'pylib'))[1]) import gyp @@ -29,12 +57,15 @@ def main(args): if 'GYP_GENERATORS' not in os.environ: os.environ['GYP_GENERATORS'] = 'ninja' - crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else '.' + crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else os.curdir - args.extend(['-D', 'crashpad_standalone=1']) - args.extend(['--include', - os.path.join(crashpad_dir, 'third_party', 'mini_chromium', - 'mini_chromium', 'build', 'common.gypi')]) + (dependencies, mini_chromium_dir) = (ChooseDependencyPath( + os.path.join(crashpad_dir, 'third_party', 'mini_chromium', + 'mini_chromium', 'build', 'common.gypi'), + os.path.join(crashpad_dir, os.pardir, 'mini_chromium', 'build', + 'common.gypi'))) + args.extend(['-D', 'crashpad_dependencies=%s' % dependencies]) + args.extend(['--include', mini_chromium_dir]) args.extend(['--depth', crashpad_dir_or_dot]) args.append(os.path.join(crashpad_dir, 'crashpad.gyp')) diff --git a/handler/handler.gyp b/handler/handler.gyp index 9a520a27..40d5977e 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -15,7 +15,7 @@ { 'includes': [ '../build/crashpad.gypi', - '../build/crashpad_in_chromium.gypi', + '../build/crashpad_dependencies.gypi', ], 'targets': [ { @@ -78,7 +78,7 @@ 'component%': 'static_library', }, 'conditions': [ - ['crashpad_in_chromium!=0 and component=="shared_library"', { + ['crashpad_dependencies=="chromium" and component=="shared_library"', { 'xcode_settings': { 'LD_RUNPATH_SEARCH_PATHS': [ # -Wl,-rpath # Get back from diff --git a/third_party/gtest/gmock.gyp b/third_party/gtest/gmock.gyp index d553bcfd..80c10ca3 100644 --- a/third_party/gtest/gmock.gyp +++ b/third_party/gtest/gmock.gyp @@ -14,10 +14,19 @@ { 'includes': [ - '../../build/crashpad_in_chromium.gypi', + '../../build/crashpad_dependencies.gypi', ], 'conditions': [ - ['crashpad_in_chromium==0', { + ['crashpad_dependencies!="chromium"', { + 'variables': { + 'conditions': [ + ['crashpad_dependencies=="standalone"', { + 'gmock_dir': 'gtest/googlemock', + }, { + 'gmock_dir': '../../../gmock', + }], + ], + }, 'target_defaults': { # gmock relies heavily on objects with static storage duration. 'xcode_settings': { @@ -38,41 +47,41 @@ 'gtest.gyp:gtest', ], 'include_dirs': [ - 'gtest/googlemock', - 'gtest/googlemock/include', + '<(gmock_dir)', + '<(gmock_dir)/include', ], 'sources': [ - 'gtest/googlemock/include/gmock/gmock-actions.h', - 'gtest/googlemock/include/gmock/gmock-cardinalities.h', - 'gtest/googlemock/include/gmock/gmock-generated-actions.h', - 'gtest/googlemock/include/gmock/gmock-generated-function-mockers.h', - 'gtest/googlemock/include/gmock/gmock-generated-matchers.h', - 'gtest/googlemock/include/gmock/gmock-generated-nice-strict.h', - 'gtest/googlemock/include/gmock/gmock-matchers.h', - 'gtest/googlemock/include/gmock/gmock-more-actions.h', - 'gtest/googlemock/include/gmock/gmock-more-matchers.h', - 'gtest/googlemock/include/gmock/gmock-spec-builders.h', - 'gtest/googlemock/include/gmock/gmock.h', - 'gtest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h', - 'gtest/googlemock/include/gmock/internal/custom/gmock-matchers.h', - 'gtest/googlemock/include/gmock/internal/custom/gmock-port.h', - 'gtest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h', - 'gtest/googlemock/include/gmock/internal/gmock-internal-utils.h', - 'gtest/googlemock/include/gmock/internal/gmock-port.h', - 'gtest/googlemock/src/gmock-all.cc', - 'gtest/googlemock/src/gmock-cardinalities.cc', - 'gtest/googlemock/src/gmock-internal-utils.cc', - 'gtest/googlemock/src/gmock-matchers.cc', - 'gtest/googlemock/src/gmock-spec-builders.cc', - 'gtest/googlemock/src/gmock.cc', + '<(gmock_dir)/include/gmock/gmock-actions.h', + '<(gmock_dir)/include/gmock/gmock-cardinalities.h', + '<(gmock_dir)/include/gmock/gmock-generated-actions.h', + '<(gmock_dir)/include/gmock/gmock-generated-function-mockers.h', + '<(gmock_dir)/include/gmock/gmock-generated-matchers.h', + '<(gmock_dir)/include/gmock/gmock-generated-nice-strict.h', + '<(gmock_dir)/include/gmock/gmock-matchers.h', + '<(gmock_dir)/include/gmock/gmock-more-actions.h', + '<(gmock_dir)/include/gmock/gmock-more-matchers.h', + '<(gmock_dir)/include/gmock/gmock-spec-builders.h', + '<(gmock_dir)/include/gmock/gmock.h', + '<(gmock_dir)/include/gmock/internal/custom/gmock-generated-actions.h', + '<(gmock_dir)/include/gmock/internal/custom/gmock-matchers.h', + '<(gmock_dir)/include/gmock/internal/custom/gmock-port.h', + '<(gmock_dir)/include/gmock/internal/gmock-generated-internal-utils.h', + '<(gmock_dir)/include/gmock/internal/gmock-internal-utils.h', + '<(gmock_dir)/include/gmock/internal/gmock-port.h', + '<(gmock_dir)/src/gmock-all.cc', + '<(gmock_dir)/src/gmock-cardinalities.cc', + '<(gmock_dir)/src/gmock-internal-utils.cc', + '<(gmock_dir)/src/gmock-matchers.cc', + '<(gmock_dir)/src/gmock-spec-builders.cc', + '<(gmock_dir)/src/gmock.cc', ], 'sources!': [ - 'gtest/googlemock/src/gmock-all.cc', + '<(gmock_dir)/src/gmock-all.cc', ], 'direct_dependent_settings': { 'include_dirs': [ - 'gtest/googlemock/include', + '<(gmock_dir)/include', ], 'conditions': [ ['clang!=0', { @@ -114,7 +123,7 @@ 'gtest.gyp:gtest', ], 'sources': [ - 'gtest/googlemock/src/gmock_main.cc', + '<(gmock_dir)/src/gmock_main.cc', ], }, { @@ -127,7 +136,7 @@ 'direct_dependent_settings': { 'type': 'executable', 'include_dirs': [ - 'gtest/googlemock', + '<(gmock_dir)', ], }, 'export_dependent_settings': [ @@ -145,19 +154,19 @@ 'gtest/googletest', ], 'sources': [ - 'gtest/googlemock/test/gmock-actions_test.cc', - 'gtest/googlemock/test/gmock-cardinalities_test.cc', - 'gtest/googlemock/test/gmock-generated-actions_test.cc', - 'gtest/googlemock/test/gmock-generated-function-mockers_test.cc', - 'gtest/googlemock/test/gmock-generated-internal-utils_test.cc', - 'gtest/googlemock/test/gmock-generated-matchers_test.cc', - 'gtest/googlemock/test/gmock-internal-utils_test.cc', - 'gtest/googlemock/test/gmock-matchers_test.cc', - 'gtest/googlemock/test/gmock-more-actions_test.cc', - 'gtest/googlemock/test/gmock-nice-strict_test.cc', - 'gtest/googlemock/test/gmock-port_test.cc', - 'gtest/googlemock/test/gmock-spec-builders_test.cc', - 'gtest/googlemock/test/gmock_test.cc', + '<(gmock_dir)/test/gmock-actions_test.cc', + '<(gmock_dir)/test/gmock-cardinalities_test.cc', + '<(gmock_dir)/test/gmock-generated-actions_test.cc', + '<(gmock_dir)/test/gmock-generated-function-mockers_test.cc', + '<(gmock_dir)/test/gmock-generated-internal-utils_test.cc', + '<(gmock_dir)/test/gmock-generated-matchers_test.cc', + '<(gmock_dir)/test/gmock-internal-utils_test.cc', + '<(gmock_dir)/test/gmock-matchers_test.cc', + '<(gmock_dir)/test/gmock-more-actions_test.cc', + '<(gmock_dir)/test/gmock-nice-strict_test.cc', + '<(gmock_dir)/test/gmock-port_test.cc', + '<(gmock_dir)/test/gmock-spec-builders_test.cc', + '<(gmock_dir)/test/gmock_test.cc', ], }, { @@ -167,9 +176,9 @@ 'gmock_main', ], 'sources': [ - 'gtest/googlemock/test/gmock_link_test.cc', - 'gtest/googlemock/test/gmock_link_test.h', - 'gtest/googlemock/test/gmock_link2_test.cc', + '<(gmock_dir)/test/gmock_link_test.cc', + '<(gmock_dir)/test/gmock_link_test.h', + '<(gmock_dir)/test/gmock_link2_test.cc', ], }, { @@ -178,7 +187,7 @@ 'gmock_test_executable', ], 'sources': [ - 'gtest/googlemock/test/gmock_stress_test.cc', + '<(gmock_dir)/test/gmock_stress_test.cc', ], }, { @@ -191,7 +200,7 @@ ], }, ], - }, { # else: crashpad_in_chromium!=0 + }, { # else: crashpad_dependencies=="chromium" 'targets': [ { 'target_name': 'gmock', diff --git a/third_party/gtest/gtest.gyp b/third_party/gtest/gtest.gyp index 2ac6ca20..d994b87b 100644 --- a/third_party/gtest/gtest.gyp +++ b/third_party/gtest/gtest.gyp @@ -14,10 +14,19 @@ { 'includes': [ - '../../build/crashpad_in_chromium.gypi', + '../../build/crashpad_dependencies.gypi', ], 'conditions': [ - ['crashpad_in_chromium==0', { + ['crashpad_dependencies!="chromium"', { + 'variables': { + 'conditions': [ + ['crashpad_dependencies=="standalone"', { + 'gtest_dir': 'gtest/googletest', + }, { + 'gtest_dir': '../../../gtest', + }], + ], + }, 'target_defaults': { # gtest relies heavily on objects with static storage duration. 'xcode_settings': { @@ -35,53 +44,70 @@ 'target_name': 'gtest', 'type': 'static_library', 'include_dirs': [ - 'gtest/googletest', - 'gtest/googletest/include', + '<(gtest_dir)', + '<(gtest_dir)/include', ], 'sources': [ - 'gtest/googletest/include/gtest/gtest-death-test.h', - 'gtest/googletest/include/gtest/gtest-message.h', - 'gtest/googletest/include/gtest/gtest-param-test.h', - 'gtest/googletest/include/gtest/gtest-printers.h', - 'gtest/googletest/include/gtest/gtest-spi.h', - 'gtest/googletest/include/gtest/gtest-test-part.h', - 'gtest/googletest/include/gtest/gtest-typed-test.h', - 'gtest/googletest/include/gtest/gtest.h', - 'gtest/googletest/include/gtest/gtest_pred_impl.h', - 'gtest/googletest/include/gtest/gtest_prod.h', - 'gtest/googletest/include/gtest/internal/custom/gtest-port.h', - 'gtest/googletest/include/gtest/internal/custom/gtest-printers.h', - 'gtest/googletest/include/gtest/internal/custom/gtest.h', - 'gtest/googletest/include/gtest/internal/gtest-death-test-internal.h', - 'gtest/googletest/include/gtest/internal/gtest-filepath.h', - 'gtest/googletest/include/gtest/internal/gtest-internal.h', - 'gtest/googletest/include/gtest/internal/gtest-linked_ptr.h', - 'gtest/googletest/include/gtest/internal/gtest-param-util-generated.h', - 'gtest/googletest/include/gtest/internal/gtest-param-util.h', - 'gtest/googletest/include/gtest/internal/gtest-port-arch.h', - 'gtest/googletest/include/gtest/internal/gtest-port.h', - 'gtest/googletest/include/gtest/internal/gtest-string.h', - 'gtest/googletest/include/gtest/internal/gtest-tuple.h', - 'gtest/googletest/include/gtest/internal/gtest-type-util.h', - 'gtest/googletest/src/gtest-all.cc', - 'gtest/googletest/src/gtest-death-test.cc', - 'gtest/googletest/src/gtest-filepath.cc', - 'gtest/googletest/src/gtest-internal-inl.h', - 'gtest/googletest/src/gtest-port.cc', - 'gtest/googletest/src/gtest-printers.cc', - 'gtest/googletest/src/gtest-test-part.cc', - 'gtest/googletest/src/gtest-typed-test.cc', - 'gtest/googletest/src/gtest.cc', + '<(gtest_dir)/include/gtest/gtest-death-test.h', + '<(gtest_dir)/include/gtest/gtest-message.h', + '<(gtest_dir)/include/gtest/gtest-param-test.h', + '<(gtest_dir)/include/gtest/gtest-printers.h', + '<(gtest_dir)/include/gtest/gtest-spi.h', + '<(gtest_dir)/include/gtest/gtest-test-part.h', + '<(gtest_dir)/include/gtest/gtest-typed-test.h', + '<(gtest_dir)/include/gtest/gtest.h', + '<(gtest_dir)/include/gtest/gtest_pred_impl.h', + '<(gtest_dir)/include/gtest/gtest_prod.h', + '<(gtest_dir)/include/gtest/internal/custom/gtest-port.h', + '<(gtest_dir)/include/gtest/internal/custom/gtest-printers.h', + '<(gtest_dir)/include/gtest/internal/custom/gtest.h', + '<(gtest_dir)/include/gtest/internal/gtest-death-test-internal.h', + '<(gtest_dir)/include/gtest/internal/gtest-filepath.h', + '<(gtest_dir)/include/gtest/internal/gtest-internal.h', + '<(gtest_dir)/include/gtest/internal/gtest-linked_ptr.h', + '<(gtest_dir)/include/gtest/internal/gtest-param-util-generated.h', + '<(gtest_dir)/include/gtest/internal/gtest-param-util.h', + '<(gtest_dir)/include/gtest/internal/gtest-port-arch.h', + '<(gtest_dir)/include/gtest/internal/gtest-port.h', + '<(gtest_dir)/include/gtest/internal/gtest-string.h', + '<(gtest_dir)/include/gtest/internal/gtest-tuple.h', + '<(gtest_dir)/include/gtest/internal/gtest-type-util.h', + '<(gtest_dir)/src/gtest-all.cc', + '<(gtest_dir)/src/gtest-death-test.cc', + '<(gtest_dir)/src/gtest-filepath.cc', + '<(gtest_dir)/src/gtest-internal-inl.h', + '<(gtest_dir)/src/gtest-port.cc', + '<(gtest_dir)/src/gtest-printers.cc', + '<(gtest_dir)/src/gtest-test-part.cc', + '<(gtest_dir)/src/gtest-typed-test.cc', + '<(gtest_dir)/src/gtest.cc', ], 'sources!': [ - 'gtest/googletest/src/gtest-all.cc', + '<(gtest_dir)/src/gtest-all.cc', ], - 'direct_dependent_settings': { 'include_dirs': [ - 'gtest/googletest/include', + '<(gtest_dir)/include', ], }, + 'conditions': [ + ['crashpad_dependencies=="external"', { + 'include_dirs': [ + '<(gtest_dir)/../..', + ], + 'defines': [ + 'GUNIT_NO_GOOGLE3=1', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(gtest_dir)/../..', + ], + 'defines': [ + 'GUNIT_NO_GOOGLE3=1', + ], + }, + }], + ], }, { 'target_name': 'gtest_main', @@ -90,7 +116,7 @@ 'gtest', ], 'sources': [ - 'gtest/googletest/src/gtest_main.cc', + '<(gtest_dir)/src/gtest_main.cc', ], }, { @@ -102,7 +128,7 @@ 'direct_dependent_settings': { 'type': 'executable', 'include_dirs': [ - 'gtest/googletest', + '<(gtest_dir)', ], }, 'export_dependent_settings': [ @@ -116,23 +142,23 @@ 'gtest_main', ], 'sources': [ - 'gtest/googletest/test/gtest-death-test_test.cc', - 'gtest/googletest/test/gtest-filepath_test.cc', - 'gtest/googletest/test/gtest-linked_ptr_test.cc', - 'gtest/googletest/test/gtest-message_test.cc', - 'gtest/googletest/test/gtest-options_test.cc', - 'gtest/googletest/test/gtest-port_test.cc', - 'gtest/googletest/test/gtest-printers_test.cc', - 'gtest/googletest/test/gtest-test-part_test.cc', - 'gtest/googletest/test/gtest-typed-test2_test.cc', - 'gtest/googletest/test/gtest-typed-test_test.cc', - 'gtest/googletest/test/gtest-typed-test_test.h', - 'gtest/googletest/test/gtest_main_unittest.cc', - 'gtest/googletest/test/gtest_pred_impl_unittest.cc', - 'gtest/googletest/test/gtest_prod_test.cc', - 'gtest/googletest/test/gtest_unittest.cc', - 'gtest/googletest/test/production.cc', - 'gtest/googletest/test/production.h', + '<(gtest_dir)/test/gtest-death-test_test.cc', + '<(gtest_dir)/test/gtest-filepath_test.cc', + '<(gtest_dir)/test/gtest-linked_ptr_test.cc', + '<(gtest_dir)/test/gtest-message_test.cc', + '<(gtest_dir)/test/gtest-options_test.cc', + '<(gtest_dir)/test/gtest-port_test.cc', + '<(gtest_dir)/test/gtest-printers_test.cc', + '<(gtest_dir)/test/gtest-test-part_test.cc', + '<(gtest_dir)/test/gtest-typed-test2_test.cc', + '<(gtest_dir)/test/gtest-typed-test_test.cc', + '<(gtest_dir)/test/gtest-typed-test_test.h', + '<(gtest_dir)/test/gtest_main_unittest.cc', + '<(gtest_dir)/test/gtest_pred_impl_unittest.cc', + '<(gtest_dir)/test/gtest_prod_test.cc', + '<(gtest_dir)/test/gtest_unittest.cc', + '<(gtest_dir)/test/production.cc', + '<(gtest_dir)/test/production.h', ], }, { @@ -141,7 +167,7 @@ 'gtest_test_executable', ], 'sources': [ - 'gtest/googletest/test/gtest_environment_test.cc', + '<(gtest_dir)/test/gtest_environment_test.cc', ], }, { @@ -150,7 +176,7 @@ 'gtest_test_executable', ], 'sources': [ - 'gtest/googletest/test/gtest-listener_test.cc', + '<(gtest_dir)/test/gtest-listener_test.cc', ], }, { @@ -159,7 +185,7 @@ 'gtest_test_executable', ], 'sources': [ - 'gtest/googletest/test/gtest_no_test_unittest.cc', + '<(gtest_dir)/test/gtest_no_test_unittest.cc', ], }, { @@ -168,9 +194,9 @@ 'gtest_test_executable', ], 'sources': [ - 'gtest/googletest/test/gtest-param-test2_test.cc', - 'gtest/googletest/test/gtest-param-test_test.cc', - 'gtest/googletest/test/gtest-param-test_test.h', + '<(gtest_dir)/test/gtest-param-test2_test.cc', + '<(gtest_dir)/test/gtest-param-test_test.cc', + '<(gtest_dir)/test/gtest-param-test_test.h', ], }, { @@ -179,7 +205,7 @@ 'gtest_test_executable', ], 'sources': [ - 'gtest/googletest/test/gtest_premature_exit_test.cc', + '<(gtest_dir)/test/gtest_premature_exit_test.cc', ], }, { @@ -188,7 +214,7 @@ 'gtest_test_executable', ], 'sources': [ - 'gtest/googletest/test/gtest_repeat_test.cc', + '<(gtest_dir)/test/gtest_repeat_test.cc', ], }, { @@ -198,7 +224,7 @@ 'gtest_main', ], 'sources': [ - 'gtest/googletest/test/gtest_sole_header_test.cc', + '<(gtest_dir)/test/gtest_sole_header_test.cc', ], }, { @@ -207,7 +233,7 @@ 'gtest_test_executable', ], 'sources': [ - 'gtest/googletest/test/gtest_stress_test.cc', + '<(gtest_dir)/test/gtest_stress_test.cc', ], }, { @@ -216,7 +242,7 @@ 'gtest_test_executable', ], 'sources': [ - 'gtest/googletest/test/gtest-unittest-api_test.cc', + '<(gtest_dir)/test/gtest-unittest-api_test.cc', ], }, { @@ -236,7 +262,7 @@ ], }, ], - }, { # else: crashpad_in_chromium!=0 + }, { # else: crashpad_dependencies=="chromium" 'targets': [ { 'target_name': 'gtest', diff --git a/third_party/mini_chromium/mini_chromium.gyp b/third_party/mini_chromium/mini_chromium.gyp index 56f897d3..bc984427 100644 --- a/third_party/mini_chromium/mini_chromium.gyp +++ b/third_party/mini_chromium/mini_chromium.gyp @@ -14,25 +14,27 @@ { 'includes': [ - '../../build/crashpad_in_chromium.gypi', + '../../build/crashpad_dependencies.gypi', ], 'targets': [ { - # To support both Crashpad’s standalone build and its in-Chromium build, - # Crashpad code depending on base should do so through this shim, which - # will either get base from mini_chromium or Chromium depending on the - # build type. + # To support Crashpad’s standalone build, its in-Chromium build, and its + # build depending on external libraries, Crashpad code depending on base + # should do so through this shim, which will either get base from + # mini_chromium, Chromium, or an external library depending on the build + # type. 'target_name': 'base', 'type': 'none', 'conditions': [ - ['crashpad_in_chromium==0', { + ['crashpad_dependencies=="standalone"', { 'dependencies': [ 'mini_chromium/base/base.gyp:base', ], 'export_dependent_settings': [ 'mini_chromium/base/base.gyp:base', ], - }, { # else: crashpad_in_chromium!=0 + }], + ['crashpad_dependencies=="chromium"', { 'dependencies': [ '<(DEPTH)/base/base.gyp:base', ], @@ -40,6 +42,14 @@ '<(DEPTH)/base/base.gyp:base', ], }], + ['crashpad_dependencies=="external"', { + 'dependencies': [ + '<(DEPTH)/../mini_chromium/base/base.gyp:base', + ], + 'export_dependent_settings': [ + '<(DEPTH)/../mini_chromium/base/base.gyp:base', + ], + }], ], }, ], From e18f6a6e66d33b7a292ebbf3e2f1994c3c188836 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 27 Jan 2016 16:49:23 -0500 Subject: [PATCH 06/16] Nest crashpad and mini_chromum deeper in the external-dependencies build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit crashpad and mini_chromium both have top-level “build” directories. These would conflict with top-level “BUILD” files in google3 when checked out on a case-sensitive filesystem. Although Crashpad’s “build” directory can be moved easily, mini_chromium’s matches Chromium’s, which is much more difficult to move. “build” is also the best and most obvious name for these directories. To avoid this problem, in the external-dependencies build, crashpad and mini_chromium are placed one level deeper, just as crashpad is in Chromium, and mini_chromium is in the standalone Crashpad build. This allows true pristine unmodified copies to be checked in to google3, without comingling locally-added files such as BUILD with external source. The directory structure adopted for the external-dependencies build is now root/crashpad/crashpad[/README] root/gmock[/include/gmock/gmock.h] root/gtest[/include/gtest/gtest.h] root/gyp[/pylib/gyp] root/mini_chromium/mini_chromium[/build/common.gypi] Change-Id: Idbc8f1b0d87da0cbceab3c15e059e839c1fb6a3f Reviewed-on: https://chromium-review.googlesource.com/323991 Reviewed-by: Robert Sesek --- build/gyp_crashpad.py | 6 +++--- third_party/gtest/gmock.gyp | 2 +- third_party/gtest/gtest.gyp | 2 +- third_party/mini_chromium/mini_chromium.gyp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build/gyp_crashpad.py b/build/gyp_crashpad.py index 7496cb1f..741c8126 100755 --- a/build/gyp_crashpad.py +++ b/build/gyp_crashpad.py @@ -47,7 +47,7 @@ crashpad_dir = (os.path.dirname(script_dir) if script_dir not in ('', os.curdir) sys.path.insert(0, ChooseDependencyPath(os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp', 'pylib'), - os.path.join(crashpad_dir, os.pardir, 'gyp', + os.path.join(crashpad_dir, os.pardir, os.pardir, 'gyp', 'pylib'))[1]) import gyp @@ -62,8 +62,8 @@ def main(args): (dependencies, mini_chromium_dir) = (ChooseDependencyPath( os.path.join(crashpad_dir, 'third_party', 'mini_chromium', 'mini_chromium', 'build', 'common.gypi'), - os.path.join(crashpad_dir, os.pardir, 'mini_chromium', 'build', - 'common.gypi'))) + os.path.join(crashpad_dir, os.pardir, os.pardir, 'mini_chromium', + 'mini_chromium', 'build', 'common.gypi'))) args.extend(['-D', 'crashpad_dependencies=%s' % dependencies]) args.extend(['--include', mini_chromium_dir]) args.extend(['--depth', crashpad_dir_or_dot]) diff --git a/third_party/gtest/gmock.gyp b/third_party/gtest/gmock.gyp index 80c10ca3..683b89e1 100644 --- a/third_party/gtest/gmock.gyp +++ b/third_party/gtest/gmock.gyp @@ -23,7 +23,7 @@ ['crashpad_dependencies=="standalone"', { 'gmock_dir': 'gtest/googlemock', }, { - 'gmock_dir': '../../../gmock', + 'gmock_dir': '../../../../gmock', }], ], }, diff --git a/third_party/gtest/gtest.gyp b/third_party/gtest/gtest.gyp index d994b87b..e01adaf5 100644 --- a/third_party/gtest/gtest.gyp +++ b/third_party/gtest/gtest.gyp @@ -23,7 +23,7 @@ ['crashpad_dependencies=="standalone"', { 'gtest_dir': 'gtest/googletest', }, { - 'gtest_dir': '../../../gtest', + 'gtest_dir': '../../../../gtest', }], ], }, diff --git a/third_party/mini_chromium/mini_chromium.gyp b/third_party/mini_chromium/mini_chromium.gyp index bc984427..f063dd0a 100644 --- a/third_party/mini_chromium/mini_chromium.gyp +++ b/third_party/mini_chromium/mini_chromium.gyp @@ -44,10 +44,10 @@ }], ['crashpad_dependencies=="external"', { 'dependencies': [ - '<(DEPTH)/../mini_chromium/base/base.gyp:base', + '../../../../mini_chromium/mini_chromium/base/base.gyp:base', ], 'export_dependent_settings': [ - '<(DEPTH)/../mini_chromium/base/base.gyp:base', + '../../../../mini_chromium/mini_chromium/base/base.gyp:base', ], }], ], From af3dc54f2a89be0a51fc096dcfeb211dbeac669a Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 14 Jan 2016 15:12:28 -0800 Subject: [PATCH 07/16] Add a field to CrashpadInfo to control indirect memory capture Change-Id: I6467aafba5d20f8a199bab0e2476f98a5318f84a Reviewed-on: https://chromium-review.googlesource.com/322245 Reviewed-by: Mark Mentovai --- client/crashpad_info.cc | 1 + client/crashpad_info.h | 19 +++++++++++++++++- snapshot/crashpad_info_client_options.cc | 3 ++- snapshot/crashpad_info_client_options.h | 3 +++ snapshot/crashpad_info_client_options_test.cc | 20 +++++++++++++++++++ snapshot/mac/process_snapshot_mac.cc | 4 ++++ snapshot/win/module_snapshot_win.cc | 4 ++++ snapshot/win/pe_image_reader.h | 3 ++- snapshot/win/process_snapshot_win.cc | 4 ++++ 9 files changed, 58 insertions(+), 3 deletions(-) diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 596f3491..46e73210 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -98,6 +98,7 @@ CrashpadInfo::CrashpadInfo() version_(kCrashpadInfoVersion), crashpad_handler_behavior_(TriState::kUnset), system_crash_reporter_forwarding_(TriState::kUnset), + gather_indirectly_referenced_memory_(TriState::kUnset), padding_0_(0), simple_annotations_(nullptr) #if !defined(NDEBUG) && defined(OS_WIN) diff --git a/client/crashpad_info.h b/client/crashpad_info.h index 1c4997ab..377d194e 100644 --- a/client/crashpad_info.h +++ b/client/crashpad_info.h @@ -97,6 +97,22 @@ struct CrashpadInfo { system_crash_reporter_forwarding_ = system_crash_reporter_forwarding; } + //! \brief Enables or disables Crashpad capturing indirectly referenced memory + //! in the minidump. + //! + //! When handling an exception, the Crashpad handler will scan all modules in + //! a process. The first one that has a CrashpadInfo structure populated with + //! a value other than #kUnset for this field will dictate whether the extra + //! memory is captured. + //! + //! This causes Crashpad to include pages of data referenced by locals or + //! other stack memory. Turning this on can increase the size of the minidump + //! significantly. + void set_gather_indirectly_referenced_memory( + TriState gather_indirectly_referenced_memory) { + gather_indirectly_referenced_memory_ = gather_indirectly_referenced_memory; + } + enum : uint32_t { kSignature = 'CPad', }; @@ -117,7 +133,8 @@ struct CrashpadInfo { uint32_t version_; // kVersion TriState crashpad_handler_behavior_; TriState system_crash_reporter_forwarding_; - uint16_t padding_0_; + TriState gather_indirectly_referenced_memory_; + uint8_t padding_0_; SimpleStringDictionary* simple_annotations_; // weak #if !defined(NDEBUG) && defined(OS_WIN) diff --git a/snapshot/crashpad_info_client_options.cc b/snapshot/crashpad_info_client_options.cc index e19b021e..4a89d84a 100644 --- a/snapshot/crashpad_info_client_options.cc +++ b/snapshot/crashpad_info_client_options.cc @@ -38,7 +38,8 @@ TriState CrashpadInfoClientOptions::TriStateFromCrashpadInfo( CrashpadInfoClientOptions::CrashpadInfoClientOptions() : crashpad_handler_behavior(TriState::kUnset), - system_crash_reporter_forwarding(TriState::kUnset) { + system_crash_reporter_forwarding(TriState::kUnset), + gather_indirectly_referenced_memory(TriState::kUnset) { } } // namespace crashpad diff --git a/snapshot/crashpad_info_client_options.h b/snapshot/crashpad_info_client_options.h index 3f9e03da..eeb1904d 100644 --- a/snapshot/crashpad_info_client_options.h +++ b/snapshot/crashpad_info_client_options.h @@ -59,6 +59,9 @@ struct CrashpadInfoClientOptions { //! \sa CrashpadInfo::set_system_crash_reporter_forwarding() TriState system_crash_reporter_forwarding; + + //! \sa CrashpadInfo::set_gather_indirectly_referenced_memory() + TriState gather_indirectly_referenced_memory; }; } // namespace crashpad diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc index 9c1f88b5..b6d27301 100644 --- a/snapshot/crashpad_info_client_options_test.cc +++ b/snapshot/crashpad_info_client_options_test.cc @@ -61,6 +61,7 @@ class ScopedUnsetCrashpadInfoOptions { ~ScopedUnsetCrashpadInfoOptions() { crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset); crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset); + crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset); } private: @@ -87,6 +88,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); ASSERT_TRUE(crashpad_info); @@ -99,6 +101,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { process_snapshot.GetCrashpadOptions(&options); EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); } { @@ -109,6 +112,18 @@ TEST(CrashpadInfoClientOptions, OneModule) { process_snapshot.GetCrashpadOptions(&options); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding); + EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); + } + + { + ScopedUnsetCrashpadInfoOptions unset(crashpad_info); + + crashpad_info->set_gather_indirectly_referenced_memory(TriState::kEnabled); + + process_snapshot.GetCrashpadOptions(&options); + EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); + EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + EXPECT_EQ(TriState::kEnabled, options.gather_indirectly_referenced_memory); } } @@ -201,6 +216,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); // Get both CrashpadInfo structures. CrashpadInfo* local_crashpad_info = CrashpadInfo::GetCrashpadInfo(); @@ -219,6 +235,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { process_snapshot.GetCrashpadOptions(&options); EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); // When more than one module sets a value, the first one in the module list // applies to the process. The local module should appear before the remote @@ -228,6 +245,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { process_snapshot.GetCrashpadOptions(&options); EXPECT_EQ(TriState::kDisabled, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); + EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); } { @@ -241,6 +259,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { process_snapshot.GetCrashpadOptions(&options); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding); + EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); // When more than one module sets a value, the first one in the module list // applies to the process. The local module should appear before the remote @@ -251,6 +270,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { process_snapshot.GetCrashpadOptions(&options); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kEnabled, options.system_crash_reporter_forwarding); + EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); } } diff --git a/snapshot/mac/process_snapshot_mac.cc b/snapshot/mac/process_snapshot_mac.cc index b37123c8..0e4b3d3b 100644 --- a/snapshot/mac/process_snapshot_mac.cc +++ b/snapshot/mac/process_snapshot_mac.cc @@ -104,6 +104,10 @@ void ProcessSnapshotMac::GetCrashpadOptions( local_options.system_crash_reporter_forwarding = module_options.system_crash_reporter_forwarding; } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + } // If non-default values have been found for all options, the loop can end // early. diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index 0553f35b..8948b2ab 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -202,6 +202,10 @@ void ModuleSnapshotWin::GetCrashpadOptionsInternal( options->system_crash_reporter_forwarding = CrashpadInfoClientOptions::TriStateFromCrashpadInfo( crashpad_info.system_crash_reporter_forwarding); + + options->gather_indirectly_referenced_memory = + CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + crashpad_info.gather_indirectly_referenced_memory); } const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const { diff --git a/snapshot/win/pe_image_reader.h b/snapshot/win/pe_image_reader.h index 4e9516b2..feffa8a8 100644 --- a/snapshot/win/pe_image_reader.h +++ b/snapshot/win/pe_image_reader.h @@ -41,7 +41,8 @@ struct CrashpadInfo { uint32_t version; uint8_t crashpad_handler_behavior; // TriState. uint8_t system_crash_reporter_forwarding; // TriState. - uint16_t padding_0; + uint8_t gather_indirectly_referenced_memory; // TriState. + uint8_t padding_0; typename Traits::Pointer simple_annotations; }; diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 8c184c26..99e52937 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -119,6 +119,10 @@ void ProcessSnapshotWin::GetCrashpadOptions( local_options.system_crash_reporter_forwarding = module_options.system_crash_reporter_forwarding; } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + } // If non-default values have been found for all options, the loop can end // early. From 83247fda6010d3507aa1dc65926d5c77fc97defc Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 28 Jan 2016 12:24:51 -0800 Subject: [PATCH 08/16] Fix Mac after af3dc54f CrashpadInfo not being initialized/propagated properly on Mac. Change-Id: I5f33a16e4e18bb1b068e0d4aeb7f2032a6cb6278 Reviewed-on: https://chromium-review.googlesource.com/324500 Reviewed-by: Mark Mentovai --- snapshot/mac/module_snapshot_mac.cc | 5 +++++ snapshot/mac/process_snapshot_mac.cc | 3 ++- snapshot/mac/process_types/crashpad_info.proctype | 5 ++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/snapshot/mac/module_snapshot_mac.cc b/snapshot/mac/module_snapshot_mac.cc index 64f04dce..dd9e8097 100644 --- a/snapshot/mac/module_snapshot_mac.cc +++ b/snapshot/mac/module_snapshot_mac.cc @@ -64,6 +64,7 @@ void ModuleSnapshotMac::GetCrashpadOptions(CrashpadInfoClientOptions* options) { if (!mach_o_image_reader_->GetCrashpadInfo(&crashpad_info)) { options->crashpad_handler_behavior = TriState::kUnset; options->system_crash_reporter_forwarding = TriState::kUnset; + options->gather_indirectly_referenced_memory = TriState::kUnset; return; } @@ -74,6 +75,10 @@ void ModuleSnapshotMac::GetCrashpadOptions(CrashpadInfoClientOptions* options) { options->system_crash_reporter_forwarding = CrashpadInfoClientOptions::TriStateFromCrashpadInfo( crashpad_info.system_crash_reporter_forwarding); + + options->gather_indirectly_referenced_memory = + CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + crashpad_info.gather_indirectly_referenced_memory); } std::string ModuleSnapshotMac::Name() const { diff --git a/snapshot/mac/process_snapshot_mac.cc b/snapshot/mac/process_snapshot_mac.cc index 0e4b3d3b..3d44e935 100644 --- a/snapshot/mac/process_snapshot_mac.cc +++ b/snapshot/mac/process_snapshot_mac.cc @@ -112,7 +112,8 @@ void ProcessSnapshotMac::GetCrashpadOptions( // If non-default values have been found for all options, the loop can end // early. if (local_options.crashpad_handler_behavior != TriState::kUnset && - local_options.system_crash_reporter_forwarding != TriState::kUnset) { + local_options.system_crash_reporter_forwarding != TriState::kUnset && + local_options.gather_indirectly_referenced_memory != TriState::kUnset) { break; } } diff --git a/snapshot/mac/process_types/crashpad_info.proctype b/snapshot/mac/process_types/crashpad_info.proctype index cdc8247f..af30d951 100644 --- a/snapshot/mac/process_types/crashpad_info.proctype +++ b/snapshot/mac/process_types/crashpad_info.proctype @@ -32,7 +32,10 @@ PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) // TriState PROCESS_TYPE_STRUCT_MEMBER(uint8_t, system_crash_reporter_forwarding) - PROCESS_TYPE_STRUCT_MEMBER(uint16_t, padding_0) + // TriState + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, gather_indirectly_referenced_memory) + + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, padding_0) // SimpleStringDictionary* PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations) From feb3aa3923dd72b1ffb6d020d7c2636757f0c203 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 14 Jan 2016 12:50:22 -0800 Subject: [PATCH 09/16] win: Capture memory pointed to by the stack Change-Id: Ide75475aa9c42edf36c3a709bfc7dfbfed68b0d3 Reviewed-on: https://chromium-review.googlesource.com/322261 Reviewed-by: Mark Mentovai --- handler/win/crashy_test_program.cc | 13 ++ snapshot/capture_memory.cc | 128 ++++++++++++++++++ snapshot/capture_memory.h | 98 ++++++++++++++ snapshot/crashpad_info_client_options_test.cc | 43 +++--- snapshot/snapshot.gyp | 6 +- snapshot/win/capture_context_memory.cc | 103 -------------- snapshot/win/capture_context_memory.h | 43 ------ snapshot/win/capture_memory_delegate_win.cc | 56 ++++++++ snapshot/win/capture_memory_delegate_win.h | 58 ++++++++ snapshot/win/end_to_end_test.py | 12 ++ snapshot/win/exception_snapshot_win.cc | 8 +- snapshot/win/process_snapshot_win.cc | 76 ++++++----- snapshot/win/process_snapshot_win.h | 6 +- snapshot/win/thread_snapshot_win.cc | 13 +- snapshot/win/thread_snapshot_win.h | 5 +- 15 files changed, 454 insertions(+), 214 deletions(-) create mode 100644 snapshot/capture_memory.cc create mode 100644 snapshot/capture_memory.h delete mode 100644 snapshot/win/capture_context_memory.cc delete mode 100644 snapshot/win/capture_context_memory.h create mode 100644 snapshot/win/capture_memory_delegate_win.cc create mode 100644 snapshot/win/capture_memory_delegate_win.h diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index 6fbd514d..f9e8501f 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -28,6 +28,7 @@ #include "base/macros.h" #include "build/build_config.h" #include "client/crashpad_client.h" +#include "client/crashpad_info.h" #include "util/win/critical_section_with_debug_info.h" #include "util/win/get_function.h" @@ -164,6 +165,18 @@ int CrashyMain(int argc, wchar_t* argv[]) { return EXIT_FAILURE; } + // Make sure data pointed to by the stack is captured. + const int kDataSize = 512; + int* pointed_to_data = new int[kDataSize]; + for (int i = 0; i < kDataSize; ++i) + pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0); + int* offset_pointer = &pointed_to_data[128]; + // Encourage the compiler to keep this variable around. + printf("%p, %p\n", offset_pointer, &offset_pointer); + + crashpad::CrashpadInfo::GetCrashpadInfo() + ->set_gather_indirectly_referenced_memory(TriState::kEnabled); + AllocateMemoryOfVariousProtections(); if (InitializeCriticalSectionWithDebugInfoIfPossible( diff --git a/snapshot/capture_memory.cc b/snapshot/capture_memory.cc new file mode 100644 index 00000000..c692798b --- /dev/null +++ b/snapshot/capture_memory.cc @@ -0,0 +1,128 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/capture_memory.h" + +#include + +#include + +#include "base/memory/scoped_ptr.h" +#include "snapshot/memory_snapshot.h" + +namespace crashpad { +namespace internal { + +namespace { + +void MaybeCaptureMemoryAround(CaptureMemory::Delegate* delegate, + uint64_t address) { + const uint64_t non_address_offset = 0x10000; + if (address < non_address_offset) + return; + + const uint64_t max_address = delegate->Is64Bit() ? + std::numeric_limits::max() : + std::numeric_limits::max(); + if (address > max_address - non_address_offset) + return; + + const uint64_t kRegisterByteOffset = 256; + const uint64_t target = address - kRegisterByteOffset; + const uint64_t size = 1024; + static_assert(kRegisterByteOffset <= size / 2, + "negative offset too large"); + auto ranges = + delegate->GetReadableRanges(CheckedRange(target, size)); + for (const auto& range : ranges) { + delegate->AddNewMemorySnapshot(range); + } +} + +template +void CaptureAtPointersInRange(uint8_t* buffer, + uint64_t buffer_size, + CaptureMemory::Delegate* delegate) { + for (uint64_t address_offset = 0; address_offset < buffer_size; + address_offset += sizeof(T)) { + uint64_t target_address = *reinterpret_cast(&buffer[address_offset]); + MaybeCaptureMemoryAround(delegate, target_address); + } +} + +} // namespace + +// static +void CaptureMemory::PointedToByContext(const CPUContext& context, + Delegate* delegate) { +#if defined(ARCH_CPU_X86_FAMILY) + if (context.architecture == kCPUArchitectureX86_64) { + MaybeCaptureMemoryAround(delegate, context.x86_64->rax); + MaybeCaptureMemoryAround(delegate, context.x86_64->rbx); + MaybeCaptureMemoryAround(delegate, context.x86_64->rcx); + MaybeCaptureMemoryAround(delegate, context.x86_64->rdx); + MaybeCaptureMemoryAround(delegate, context.x86_64->rdi); + MaybeCaptureMemoryAround(delegate, context.x86_64->rsi); + MaybeCaptureMemoryAround(delegate, context.x86_64->rbp); + MaybeCaptureMemoryAround(delegate, context.x86_64->r8); + MaybeCaptureMemoryAround(delegate, context.x86_64->r9); + MaybeCaptureMemoryAround(delegate, context.x86_64->r10); + MaybeCaptureMemoryAround(delegate, context.x86_64->r11); + MaybeCaptureMemoryAround(delegate, context.x86_64->r12); + MaybeCaptureMemoryAround(delegate, context.x86_64->r13); + MaybeCaptureMemoryAround(delegate, context.x86_64->r14); + MaybeCaptureMemoryAround(delegate, context.x86_64->r15); + MaybeCaptureMemoryAround(delegate, context.x86_64->rip); + } else { + MaybeCaptureMemoryAround(delegate, context.x86->eax); + MaybeCaptureMemoryAround(delegate, context.x86->ebx); + MaybeCaptureMemoryAround(delegate, context.x86->ecx); + MaybeCaptureMemoryAround(delegate, context.x86->edx); + MaybeCaptureMemoryAround(delegate, context.x86->edi); + MaybeCaptureMemoryAround(delegate, context.x86->esi); + MaybeCaptureMemoryAround(delegate, context.x86->ebp); + MaybeCaptureMemoryAround(delegate, context.x86->eip); + } +#else +#error non-x86 +#endif +} + +// static +void CaptureMemory::PointedToByMemoryRange(const MemorySnapshot& memory, + Delegate* delegate) { + if (memory.Size() == 0) + return; + + const size_t alignment = + delegate->Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t); + if (memory.Address() % alignment != 0 || memory.Size() % alignment != 0) { + LOG(ERROR) << "unaligned range"; + return; + } + + scoped_ptr buffer(new uint8_t[memory.Size()]); + if (!delegate->ReadMemory(memory.Address(), memory.Size(), buffer.get())) { + LOG(ERROR) << "ReadMemory"; + return; + } + + if (delegate->Is64Bit()) + CaptureAtPointersInRange(buffer.get(), memory.Size(), delegate); + else + CaptureAtPointersInRange(buffer.get(), memory.Size(), delegate); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/capture_memory.h b/snapshot/capture_memory.h new file mode 100644 index 00000000..ef5f4ed9 --- /dev/null +++ b/snapshot/capture_memory.h @@ -0,0 +1,98 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_ +#define CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_ + +#include + +#include + +#include "snapshot/cpu_context.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { + +class MemorySnapshot; + +namespace internal { + +class CaptureMemory { + public: + //! \brief An interface to a platform-specific process reader. + class Delegate { + public: + virtual ~Delegate() {} + + //! \return `true` if the target process is a 64-bit process. + virtual bool Is64Bit() const = 0; + + //! \brief Attempts to read \a num_bytes bytes from the target process + //! starting at address \a at into \a into. + //! + //! \return `true` if the entire region could be read, or `false` with an + //! error logged. + virtual bool ReadMemory(uint64_t at, + uint64_t num_bytes, + void* into) const = 0; + + //! \brief Given a range to be read from the target process, returns a + //! vector + //! of ranges, representing the readable portions of the original range. + //! + //! \param[in] range The range being identified. + //! + //! \return A vector of ranges corresponding to the portion of \a range that + //! is readable. + virtual std::vector> GetReadableRanges( + const CheckedRange& range) const = 0; + + //! \brief Adds the given range representing a memory snapshot in the target + //! process to the result. + virtual void AddNewMemorySnapshot( + const CheckedRange& range) = 0; + }; + + //! \brief For all registers that appear to be pointer-like in \a context, + //! captures a small amount of memory near their pointed to location. + //! + //! "Pointer-like" in this context means not too close to zero (signed or + //! unsigned) so that there's a reasonable chance that the value is a pointer. + //! + //! \param[in] context The context to inspect. + //! \param[in] process_reader A MemoryCaptureProcessReader to read from the + //! target process, and that handles adding new ranges. + static void PointedToByContext(const CPUContext& context, Delegate* delegate); + + //! \brief For all pointer-like values in a memory range of the target + //! process, + //! captures a small amount of memory near the pointed to location. + //! + //! \param[in] memory An existing MemorySnapshot of the range to search. The + //! base address and size must be pointer-aligned and an integral number + //! of + //! pointers long. + //! \param[in] process_reader A MemoryCaptureProcessReader to read from the + //! target process, and that handles adding new ranges. + static void PointedToByMemoryRange(const MemorySnapshot& memory, + Delegate* delegate); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureMemory); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_ diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc index b6d27301..50ba6310 100644 --- a/snapshot/crashpad_info_client_options_test.cc +++ b/snapshot/crashpad_info_client_options_test.cc @@ -70,14 +70,13 @@ class ScopedUnsetCrashpadInfoOptions { DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfoOptions); }; -TEST(CrashpadInfoClientOptions, OneModule) { - // Make sure that the initial state has all values unset. +CrashpadInfoClientOptions SelfProcessSnapshotAndGetCrashpadOptions() { #if defined(OS_MACOSX) ProcessSnapshotMac process_snapshot; - ASSERT_TRUE(process_snapshot.Initialize(mach_task_self())); + EXPECT_TRUE(process_snapshot.Initialize(mach_task_self())); #elif defined(OS_WIN) ProcessSnapshotWin process_snapshot; - ASSERT_TRUE(process_snapshot.Initialize( + EXPECT_TRUE(process_snapshot.Initialize( GetCurrentProcess(), ProcessSuspensionState::kRunning, 0)); #else #error Port. @@ -85,6 +84,12 @@ TEST(CrashpadInfoClientOptions, OneModule) { CrashpadInfoClientOptions options; process_snapshot.GetCrashpadOptions(&options); + return options; +} + +TEST(CrashpadInfoClientOptions, OneModule) { + // Make sure that the initial state has all values unset. + auto options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); @@ -98,7 +103,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -109,7 +114,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -120,7 +125,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { crashpad_info->set_gather_indirectly_referenced_memory(TriState::kEnabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kEnabled, options.gather_indirectly_referenced_memory); @@ -199,21 +204,9 @@ TEST(CrashpadInfoClientOptions, TwoModules) { dl_handle.LookUpSymbol("TestModule_GetCrashpadInfo"); ASSERT_TRUE(TestModule_GetCrashpadInfo); + auto options = SelfProcessSnapshotAndGetCrashpadOptions(); + // Make sure that the initial state has all values unset. -#if defined(OS_MACOSX) - ProcessSnapshotMac process_snapshot; - ASSERT_TRUE(process_snapshot.Initialize(mach_task_self())); -#elif defined(OS_WIN) - ProcessSnapshotWin process_snapshot; - ASSERT_TRUE(process_snapshot.Initialize( - GetCurrentProcess(), ProcessSuspensionState::kRunning, 0)); -#else -#error Port. -#endif // OS_MACOSX - - CrashpadInfoClientOptions options; - process_snapshot.GetCrashpadOptions(&options); - EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -232,7 +225,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { // When only one module sets a value, it applies to the entire process. remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -242,7 +235,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { // module, because the local module loaded the remote module. local_crashpad_info->set_crashpad_handler_behavior(TriState::kDisabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kDisabled, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -256,7 +249,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { remote_crashpad_info->set_system_crash_reporter_forwarding( TriState::kDisabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); @@ -267,7 +260,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { local_crashpad_info->set_system_crash_reporter_forwarding( TriState::kEnabled); - process_snapshot.GetCrashpadOptions(&options); + options = SelfProcessSnapshotAndGetCrashpadOptions(); EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior); EXPECT_EQ(TriState::kEnabled, options.system_crash_reporter_forwarding); EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory); diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 0154495d..d0011528 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -30,6 +30,8 @@ '..', ], 'sources': [ + 'capture_memory.cc', + 'capture_memory.h', 'cpu_architecture.h', 'cpu_context.cc', 'cpu_context.h', @@ -89,12 +91,12 @@ 'process_snapshot.h', 'system_snapshot.h', 'thread_snapshot.h', - 'win/capture_context_memory.cc', - 'win/capture_context_memory.h', 'win/cpu_context_win.cc', 'win/cpu_context_win.h', 'win/exception_snapshot_win.cc', 'win/exception_snapshot_win.h', + 'win/capture_memory_delegate_win.cc', + 'win/capture_memory_delegate_win.h', 'win/memory_map_region_snapshot_win.cc', 'win/memory_map_region_snapshot_win.h', 'win/memory_snapshot_win.cc', diff --git a/snapshot/win/capture_context_memory.cc b/snapshot/win/capture_context_memory.cc deleted file mode 100644 index e4469008..00000000 --- a/snapshot/win/capture_context_memory.cc +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2016 The Crashpad Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "snapshot/win/capture_context_memory.h" - -#include - -#include - -#include "snapshot/win/memory_snapshot_win.h" -#include "snapshot/win/process_reader_win.h" - -namespace crashpad { -namespace internal { - -namespace { - -void MaybeCaptureMemoryAround(ProcessReaderWin* process_reader, - WinVMAddress address, - PointerVector* into) { - const WinVMAddress non_address_offset = 0x10000; - if (address < non_address_offset) - return; - if (process_reader->Is64Bit()) { - if (address >= std::numeric_limits::max() - non_address_offset) - return; - } else { - if (address >= std::numeric_limits::max() - non_address_offset) - return; - } - - const WinVMSize kRegisterByteOffset = 32; - const WinVMAddress target = address - kRegisterByteOffset; - const WinVMSize size = 128; - auto ranges = process_reader->GetProcessInfo().GetReadableRanges( - CheckedRange(target, size)); - for (const auto& range : ranges) { - internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin(); - snapshot->Initialize(process_reader, range.base(), range.size()); - into->push_back(snapshot); - } -} - -} // namespace - -void CaptureMemoryPointedToByContext(const CPUContext& context, - ProcessReaderWin* process_reader, - const ProcessReaderWin::Thread& thread, - PointerVector* into) { -#if defined(ARCH_CPU_X86_64) - if (context.architecture == kCPUArchitectureX86_64) { - MaybeCaptureMemoryAround(process_reader, context.x86_64->rax, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rbx, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rcx, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rdx, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rdi, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rsi, into); - if (context.x86_64->rbp < thread.stack_region_address || - context.x86_64->rbp >= - thread.stack_region_address + thread.stack_region_size) { - MaybeCaptureMemoryAround(process_reader, context.x86_64->rbp, into); - } - MaybeCaptureMemoryAround(process_reader, context.x86_64->r8, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r9, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r10, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r11, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r12, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r13, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r14, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->r15, into); - MaybeCaptureMemoryAround(process_reader, context.x86_64->rip, into); - } else { -#endif - MaybeCaptureMemoryAround(process_reader, context.x86->eax, into); - MaybeCaptureMemoryAround(process_reader, context.x86->ebx, into); - MaybeCaptureMemoryAround(process_reader, context.x86->ecx, into); - MaybeCaptureMemoryAround(process_reader, context.x86->edx, into); - MaybeCaptureMemoryAround(process_reader, context.x86->edi, into); - MaybeCaptureMemoryAround(process_reader, context.x86->esi, into); - if (context.x86->ebp < thread.stack_region_address || - context.x86->ebp >= - thread.stack_region_address + thread.stack_region_size) { - MaybeCaptureMemoryAround(process_reader, context.x86->ebp, into); - } - MaybeCaptureMemoryAround(process_reader, context.x86->eip, into); -#if defined(ARCH_CPU_X86_64) - } -#endif -} - -} // namespace internal -} // namespace crashpad diff --git a/snapshot/win/capture_context_memory.h b/snapshot/win/capture_context_memory.h deleted file mode 100644 index d4f4e038..00000000 --- a/snapshot/win/capture_context_memory.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 The Crashpad Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_ -#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_ - -#include "snapshot/cpu_context.h" -#include "snapshot/win/process_reader_win.h" -#include "util/stdlib/pointer_container.h" - -namespace crashpad { -namespace internal { - -class MemorySnapshotWin; - -//! \brief For all registers that appear to be pointer-like in \a context, -//! captures a small amount of memory near their pointed to location. -//! -//! \param[in] context The context to inspect. -//! \param[in] process_reader A ProcessReaderWin to read from the target -//! process. -//! \param[in] thread The thread to which the context belongs. -//! \param[out] into A vector of pointers to append new ranges to. -void CaptureMemoryPointedToByContext(const CPUContext& context, - ProcessReaderWin* process_reader, - const ProcessReaderWin::Thread& thread, - PointerVector* into); - -} // namespace internal -} // namespace crashpad - -#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_ diff --git a/snapshot/win/capture_memory_delegate_win.cc b/snapshot/win/capture_memory_delegate_win.cc new file mode 100644 index 00000000..1eaa4258 --- /dev/null +++ b/snapshot/win/capture_memory_delegate_win.cc @@ -0,0 +1,56 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/win/capture_memory_delegate_win.h" + +#include "snapshot/win/memory_snapshot_win.h" + +namespace crashpad { +namespace internal { + +CaptureMemoryDelegateWin::CaptureMemoryDelegateWin( + ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& thread, + PointerVector* snapshots) + : stack_(thread.stack_region_address, thread.stack_region_size), + process_reader_(process_reader), + snapshots_(snapshots) {} + +bool CaptureMemoryDelegateWin::Is64Bit() const { + return process_reader_->Is64Bit(); +} + +bool CaptureMemoryDelegateWin::ReadMemory(uint64_t at, + uint64_t num_bytes, + void* into) const { + return process_reader_->ReadMemory(at, num_bytes, into); +} + +std::vector> CaptureMemoryDelegateWin::GetReadableRanges( + const CheckedRange& range) const { + return process_reader_->GetProcessInfo().GetReadableRanges(range); +} + +void CaptureMemoryDelegateWin::AddNewMemorySnapshot( + const CheckedRange& range) { + // Don't bother storing this memory if it points back into the stack. + if (stack_.ContainsRange(range)) + return; + internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin(); + snapshot->Initialize(process_reader_, range.base(), range.size()); + snapshots_->push_back(snapshot); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/win/capture_memory_delegate_win.h b/snapshot/win/capture_memory_delegate_win.h new file mode 100644 index 00000000..e408e549 --- /dev/null +++ b/snapshot/win/capture_memory_delegate_win.h @@ -0,0 +1,58 @@ + // Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_ + +#include "snapshot/capture_memory.h" + +#include "snapshot/win/process_reader_win.h" +#include "util/stdlib/pointer_container.h" + +namespace crashpad { +namespace internal { + +class MemorySnapshotWin; + +class CaptureMemoryDelegateWin : public CaptureMemory::Delegate { + public: + //! \brief A MemoryCaptureDelegate for Windows. + //! + //! \param[in] process_reader A ProcessReaderWin for the target process. + //! \param[in] thread The thread being inspected. Memory ranges overlapping + //! this thread's stack will be ignored on the assumption that they're + //! already captured elsewhere. + //! \param[in] snapshots A vector of MemorySnapshotWin to which the captured + //! memory will be added. + CaptureMemoryDelegateWin(ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& thread, + PointerVector* snapshots); + + // MemoryCaptureDelegate: + bool Is64Bit() const override; + bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override; + std::vector> GetReadableRanges( + const CheckedRange& range) const override; + void AddNewMemorySnapshot(const CheckedRange& range); + + private: + CheckedRange stack_; + ProcessReaderWin* process_reader_; + PointerVector* snapshots_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_ diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index 34343ba6..bf4f3034 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -247,10 +247,22 @@ def RunTests(cdb_path, r'FreeOwnStackAndBreak.*\nquit:', 'at correct location, no additional stack entries') + # Switch to the other thread after jumping to the exception, and examine + # memory. out = CdbRun(cdb_path, dump_path, '.ecxr; ~1s; db /c14 edi') out.Check(r'63 62 61 60 5f 5e 5d 5c-5b 5a 59 58 57 56 55 54 53 52 51 50', 'data pointed to by registers captured') + # Move up one stack frame after jumping to the exception, and examine memory. + out = CdbRun(cdb_path, dump_path, + '.ecxr; .f+; dd /c100 poi(offset_pointer)-20') + out.Check(r'80000078 00000079 8000007a 0000007b 8000007c 0000007d 8000007e ' + r'0000007f 80000080 00000081 80000082 00000083 80000084 00000085 ' + r'80000086 00000087 80000088 00000089 8000008a 0000008b 8000008c ' + r'0000008d 8000008e 0000008f 80000090 00000091 80000092 00000093 ' + r'80000094 00000095 80000096 00000097', + 'data pointed to by stack captured') + if z7_dump_path: out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm') out.Check('This dump file has an exception of interest stored in it', diff --git a/snapshot/win/exception_snapshot_win.cc b/snapshot/win/exception_snapshot_win.cc index 9ec16f69..ea44f0fc 100644 --- a/snapshot/win/exception_snapshot_win.cc +++ b/snapshot/win/exception_snapshot_win.cc @@ -14,9 +14,10 @@ #include "snapshot/win/exception_snapshot_win.h" +#include "snapshot/capture_memory.h" #include "snapshot/memory_snapshot.h" -#include "snapshot/win/capture_context_memory.h" #include "snapshot/win/cpu_context_win.h" +#include "snapshot/win/capture_memory_delegate_win.h" #include "snapshot/win/memory_snapshot_win.h" #include "snapshot/win/process_reader_win.h" #include "util/win/nt_internals.h" @@ -89,8 +90,9 @@ bool ExceptionSnapshotWin::Initialize(ProcessReaderWin* process_reader, InitializeX86Context(context_record, context_.x86); } - CaptureMemoryPointedToByContext( - context_, process_reader, *thread, &extra_memory_); + CaptureMemoryDelegateWin capture_memory_delegate( + process_reader, *thread, &extra_memory_); + CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); INITIALIZATION_STATE_SET_VALID(initialized_); return true; diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 99e52937..535c9a73 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -38,6 +38,7 @@ ProcessSnapshotWin::ProcessSnapshotWin() client_id_(), annotations_simple_map_(), snapshot_time_(), + options_(), initialized_() { } @@ -65,9 +66,13 @@ bool ProcessSnapshotWin::Initialize( debug_critical_section_address); } - InitializeThreads(); InitializeModules(); + GetCrashpadOptionsInternal(&options_); + + InitializeThreads(options_.gather_indirectly_referenced_memory == + TriState::kEnabled); + for (const MEMORY_BASIC_INFORMATION64& mbi : process_reader_.GetProcessInfo().MemoryInfo()) { memory_map_.push_back(new internal::MemoryMapRegionSnapshotWin(mbi)); @@ -104,35 +109,7 @@ bool ProcessSnapshotWin::InitializeException( void ProcessSnapshotWin::GetCrashpadOptions( CrashpadInfoClientOptions* options) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - - CrashpadInfoClientOptions local_options; - - for (internal::ModuleSnapshotWin* module : modules_) { - CrashpadInfoClientOptions module_options; - module->GetCrashpadOptions(&module_options); - - if (local_options.crashpad_handler_behavior == TriState::kUnset) { - local_options.crashpad_handler_behavior = - module_options.crashpad_handler_behavior; - } - if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { - local_options.system_crash_reporter_forwarding = - module_options.system_crash_reporter_forwarding; - } - if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { - local_options.gather_indirectly_referenced_memory = - module_options.gather_indirectly_referenced_memory; - } - - // If non-default values have been found for all options, the loop can end - // early. - if (local_options.crashpad_handler_behavior != TriState::kUnset && - local_options.system_crash_reporter_forwarding != TriState::kUnset) { - break; - } - } - - *options = local_options; + *options = options_; } pid_t ProcessSnapshotWin::ProcessID() const { @@ -238,13 +215,16 @@ std::vector ProcessSnapshotWin::ExtraMemory() const { return extra_memory; } -void ProcessSnapshotWin::InitializeThreads() { +void ProcessSnapshotWin::InitializeThreads( + bool gather_indirectly_referenced_memory) { const std::vector& process_reader_threads = process_reader_.Threads(); for (const ProcessReaderWin::Thread& process_reader_thread : process_reader_threads) { auto thread = make_scoped_ptr(new internal::ThreadSnapshotWin()); - if (thread->Initialize(&process_reader_, process_reader_thread)) { + if (thread->Initialize(&process_reader_, + process_reader_thread, + gather_indirectly_referenced_memory)) { threads_.push_back(thread.release()); } } @@ -262,6 +242,38 @@ void ProcessSnapshotWin::InitializeModules() { } } +void ProcessSnapshotWin::GetCrashpadOptionsInternal( + CrashpadInfoClientOptions* options) { + CrashpadInfoClientOptions local_options; + + for (internal::ModuleSnapshotWin* module : modules_) { + CrashpadInfoClientOptions module_options; + module->GetCrashpadOptions(&module_options); + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset) { + break; + } + } + + *options = local_options; +} + template void ProcessSnapshotWin::InitializePebData( WinVMAddress debug_critical_section_address) { diff --git a/snapshot/win/process_snapshot_win.h b/snapshot/win/process_snapshot_win.h index 64e73da1..c2307bee 100644 --- a/snapshot/win/process_snapshot_win.h +++ b/snapshot/win/process_snapshot_win.h @@ -141,11 +141,14 @@ class ProcessSnapshotWin final : public ProcessSnapshot { private: // Initializes threads_ on behalf of Initialize(). - void InitializeThreads(); + void InitializeThreads(bool gather_indirectly_referenced_memory); // Initializes modules_ on behalf of Initialize(). void InitializeModules(); + // Initializes options_ on behalf of Initialize(). + void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options); + // Initializes various memory blocks reachable from the PEB on behalf of // Initialize(). template @@ -186,6 +189,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot { UUID client_id_; std::map annotations_simple_map_; timeval snapshot_time_; + CrashpadInfoClientOptions options_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotWin); diff --git a/snapshot/win/thread_snapshot_win.cc b/snapshot/win/thread_snapshot_win.cc index f3446e98..a240eb2a 100644 --- a/snapshot/win/thread_snapshot_win.cc +++ b/snapshot/win/thread_snapshot_win.cc @@ -17,8 +17,9 @@ #include #include "base/logging.h" -#include "snapshot/win/capture_context_memory.h" +#include "snapshot/capture_memory.h" #include "snapshot/win/cpu_context_win.h" +#include "snapshot/win/capture_memory_delegate_win.h" #include "snapshot/win/process_reader_win.h" namespace crashpad { @@ -38,7 +39,8 @@ ThreadSnapshotWin::~ThreadSnapshotWin() { bool ThreadSnapshotWin::Initialize( ProcessReaderWin* process_reader, - const ProcessReaderWin::Thread& process_reader_thread) { + const ProcessReaderWin::Thread& process_reader_thread, + bool gather_indirectly_referenced_memory) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); thread_ = process_reader_thread; @@ -76,8 +78,11 @@ bool ThreadSnapshotWin::Initialize( InitializeX86Context(process_reader_thread.context.native, context_.x86); #endif // ARCH_CPU_X86_64 - CaptureMemoryPointedToByContext( - context_, process_reader, thread_, &pointed_to_memory_); + CaptureMemoryDelegateWin capture_memory_delegate( + process_reader, thread_, &pointed_to_memory_); + CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); + if (gather_indirectly_referenced_memory) + CaptureMemory::PointedToByMemoryRange(stack_, &capture_memory_delegate); INITIALIZATION_STATE_SET_VALID(initialized_); return true; diff --git a/snapshot/win/thread_snapshot_win.h b/snapshot/win/thread_snapshot_win.h index 74e6bc6e..d746ddb4 100644 --- a/snapshot/win/thread_snapshot_win.h +++ b/snapshot/win/thread_snapshot_win.h @@ -48,11 +48,14 @@ class ThreadSnapshotWin final : public ThreadSnapshot { //! the thread. //! \param[in] process_reader_thread The thread within the ProcessReaderWin //! for which the snapshot should be created. + //! \param[in] gather_indirectly_referenced_memory If `true`, adds extra + //! memory regions to the snapshot pointed to by the thread's stack. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. bool Initialize(ProcessReaderWin* process_reader, - const ProcessReaderWin::Thread& process_reader_thread); + const ProcessReaderWin::Thread& process_reader_thread, + bool gather_indirectly_referenced_memory); // ThreadSnapshot: From a392a2c3ed480cd80b2608ca9349a4e28ad7a6dd Mon Sep 17 00:00:00 2001 From: Andrew Bonventre Date: Mon, 1 Feb 2016 16:09:27 -0800 Subject: [PATCH 10/16] Update codereview.settings to set --squash as the default for git cl Change-Id: I4d5ee3a8a3a39b34bee6ad616646966e986132b1 Reviewed-on: https://chromium-review.googlesource.com/324958 Reviewed-by: Mark Mentovai --- codereview.settings | 1 + 1 file changed, 1 insertion(+) diff --git a/codereview.settings b/codereview.settings index 38784224..685c904d 100644 --- a/codereview.settings +++ b/codereview.settings @@ -13,6 +13,7 @@ # limitations under the License. GERRIT_HOST: True +GERRIT_SQUASH_UPLOADS: True CODE_REVIEW_SERVER: https://canary-chromium-review.googlesource.com/ VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/ PROJECT: crashpad From badfacccee01895719d2aeb3ac50f64854abf10c Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 11 Feb 2016 17:19:30 -0800 Subject: [PATCH 11/16] win: Add support for capturing unloaded modules R=mark@chromium.org BUG=crashpad:89 Change-Id: Ib6a67147e538811168d68f14a457fdceab30c02e Reviewed-on: https://chromium-review.googlesource.com/327231 Reviewed-by: Mark Mentovai --- compat/non_win/dbghelp.h | 58 +++++ handler/win/crashy_test_program.cc | 10 + minidump/minidump.gyp | 2 + minidump/minidump_extensions.h | 5 + minidump/minidump_file_writer.cc | 9 + minidump/minidump_test.gyp | 1 + minidump/minidump_unloaded_module_writer.cc | 203 ++++++++++++++++++ minidump/minidump_unloaded_module_writer.h | 154 +++++++++++++ .../minidump_unloaded_module_writer_test.cc | 162 ++++++++++++++ minidump/test/minidump_writable_test_util.cc | 17 ++ minidump/test/minidump_writable_test_util.h | 7 + snapshot/mac/process_snapshot_mac.cc | 6 + snapshot/mac/process_snapshot_mac.h | 2 + .../minidump/process_snapshot_minidump.cc | 8 + snapshot/minidump/process_snapshot_minidump.h | 3 + snapshot/process_snapshot.h | 7 + snapshot/snapshot.gyp | 2 + snapshot/test/test_process_snapshot.cc | 5 + snapshot/test/test_process_snapshot.h | 12 ++ snapshot/unloaded_module_snapshot.cc | 33 +++ snapshot/unloaded_module_snapshot.h | 61 ++++++ snapshot/win/end_to_end_test.py | 5 + snapshot/win/process_snapshot_win.cc | 55 +++++ snapshot/win/process_snapshot_win.h | 6 + util/win/nt_internals.cc | 16 ++ util/win/nt_internals.h | 22 ++ 26 files changed, 871 insertions(+) create mode 100644 minidump/minidump_unloaded_module_writer.cc create mode 100644 minidump/minidump_unloaded_module_writer.h create mode 100644 minidump/minidump_unloaded_module_writer_test.cc create mode 100644 snapshot/unloaded_module_snapshot.cc create mode 100644 snapshot/unloaded_module_snapshot.h diff --git a/compat/non_win/dbghelp.h b/compat/non_win/dbghelp.h index ecf8bab8..2b7c974c 100644 --- a/compat/non_win/dbghelp.h +++ b/compat/non_win/dbghelp.h @@ -160,6 +160,9 @@ enum MINIDUMP_STREAM_TYPE { //! \brief The stream contains information about active `HANDLE`s. HandleDataStream = 12, + //! \brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST. + UnloadedModuleListStream = 14, + //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, //! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4. //! @@ -674,6 +677,9 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR_2 }; //! \brief Represents the header for a handle data stream. +//! +//! A list of MINIDUMP_HANDLE_DESCRIPTOR or MINIDUMP_HANDLE_DESCRIPTOR_2 +//! structures will immediately follow in the stream. struct __attribute((packed, aligned(4))) MINIDUMP_HANDLE_DATA_STREAM { //! \brief The size of the header information for the stream, in bytes. This //! value is `sizeof(MINIDUMP_HANDLE_DATA_STREAM)`. @@ -691,6 +697,58 @@ struct __attribute((packed, aligned(4))) MINIDUMP_HANDLE_DATA_STREAM { uint32_t Reserved; }; +//! \brief Information about a specific module that was recorded as being +//! unloaded at the time the snapshot was taken. +//! +//! An unloaded module may be a shared library or a loadable module. +//! +//! \sa MINIDUMP_UNLOADED_MODULE_LIST +struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE { + //! \brief The base address where the module was loaded in the address space + //! of the process that the minidump file contains a snapshot of. + uint64_t BaseOfImage; + + //! \brief The size of the unloaded module. + uint32_t SizeOfImage; + + //! \brief The module’s checksum, or `0` if unknown. + //! + //! On Windows, this field comes from the `CheckSum` field of the module’s + //! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at + //! the time the module was linked. + uint32_t CheckSum; + + //! \brief The module’s timestamp, in `time_t` units, seconds since the POSIX + //! epoch. + //! + //! On Windows, this field comes from the `TimeDateStamp` field of the + //! module’s `IMAGE_HEADER` structure. It reflects the timestamp at the time + //! the module was linked. + uint32_t TimeDateStamp; + + //! \brief ::RVA of a MINIDUMP_STRING containing the module’s path or file + //! name. + RVA ModuleNameRva; +}; + +//! \brief Information about all modules recorded as unloaded when the snapshot +//! was taken. +//! +//! A list of MINIDUMP_UNLOADED_MODULE structures will immediately follow in the +//! stream. +struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST { + //! \brief The size of the header information for the stream, in bytes. This + //! value is `sizeof(MINIDUMP_UNLOADED_MODULE_LIST)`. + uint32_t SizeOfHeader; + + //! \brief The size of a descriptor in the stream, in bytes. This value is + //! `sizeof(MINIDUMP_UNLOADED_MODULE)`. + uint32_t SizeOfEntry; + + //! \brief The number of entries in the stream. + uint32_t NumberOfEntries; +}; + //! \anchor MINIDUMP_MISCx //! \name MINIDUMP_MISC* //! diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index f9e8501f..51ff0129 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -165,6 +165,16 @@ int CrashyMain(int argc, wchar_t* argv[]) { return EXIT_FAILURE; } + // Load and unload some uncommonly used modules so we can see them in the list + // reported by `lm`. At least two so that we confirm we got the size of + // RTL_UNLOAD_EVENT_TRACE right. + CHECK(GetModuleHandle(L"lz32.dll") == nullptr); + CHECK(GetModuleHandle(L"wmerror.dll") == nullptr); + HMODULE lz32 = LoadLibrary(L"lz32.dll"); + HMODULE wmerror = LoadLibrary(L"wmerror.dll"); + FreeLibrary(lz32); + FreeLibrary(wmerror); + // Make sure data pointed to by the stack is captured. const int kDataSize = 512; int* pointed_to_data = new int[kDataSize]; diff --git a/minidump/minidump.gyp b/minidump/minidump.gyp index 0e7fb0ad..3f714668 100644 --- a/minidump/minidump.gyp +++ b/minidump/minidump.gyp @@ -70,6 +70,8 @@ 'minidump_thread_id_map.h', 'minidump_thread_writer.cc', 'minidump_thread_writer.h', + 'minidump_unloaded_module_writer.cc', + 'minidump_unloaded_module_writer.h', 'minidump_writable.cc', 'minidump_writable.h', 'minidump_writer_util.cc', diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index 5e43055a..68fa6e04 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -76,6 +76,11 @@ enum MinidumpStreamType : uint32_t { //! \sa HandleDataStream kMinidumpStreamTypeHandleData = HandleDataStream, + //! \brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST. + //! + //! \sa UnloadedModuleListStream + kMinidumpStreamTypeUnloadedModuleList = UnloadedModuleListStream, + //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, //! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4. //! diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index 9b540d32..0c79c78d 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -27,6 +27,7 @@ #include "minidump/minidump_system_info_writer.h" #include "minidump/minidump_thread_id_map.h" #include "minidump/minidump_thread_writer.h" +#include "minidump/minidump_unloaded_module_writer.h" #include "minidump/minidump_writer_util.h" #include "snapshot/exception_snapshot.h" #include "snapshot/process_snapshot.h" @@ -95,6 +96,14 @@ void MinidumpFileWriter::InitializeFromSnapshot( module_list->InitializeFromSnapshot(process_snapshot->Modules()); AddStream(std::move(module_list)); + auto unloaded_modules = process_snapshot->UnloadedModules(); + if (!unloaded_modules.empty()) { + auto unloaded_module_list = + make_scoped_ptr(new MinidumpUnloadedModuleListWriter()); + unloaded_module_list->InitializeFromSnapshot(unloaded_modules); + AddStream(std::move(unloaded_module_list)); + } + auto crashpad_info = make_scoped_ptr(new MinidumpCrashpadInfoWriter()); crashpad_info->InitializeFromSnapshot(process_snapshot); diff --git a/minidump/minidump_test.gyp b/minidump/minidump_test.gyp index 6f8dc198..30a179f1 100644 --- a/minidump/minidump_test.gyp +++ b/minidump/minidump_test.gyp @@ -49,6 +49,7 @@ 'minidump_system_info_writer_test.cc', 'minidump_thread_id_map_test.cc', 'minidump_thread_writer_test.cc', + 'minidump_unloaded_module_writer_test.cc', 'minidump_writable_test.cc', 'test/minidump_context_test_util.cc', 'test/minidump_context_test_util.h', diff --git a/minidump/minidump_unloaded_module_writer.cc b/minidump/minidump_unloaded_module_writer.cc new file mode 100644 index 00000000..7a520ba7 --- /dev/null +++ b/minidump/minidump_unloaded_module_writer.cc @@ -0,0 +1,203 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "minidump/minidump_unloaded_module_writer.h" + +#include + +#include "minidump/minidump_writer_util.h" +#include "util/file/file_writer.h" +#include "util/numeric/in_range_cast.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpUnloadedModuleWriter::MinidumpUnloadedModuleWriter() + : MinidumpWritable(), unloaded_module_(), name_() {} + +MinidumpUnloadedModuleWriter::~MinidumpUnloadedModuleWriter() { +} + +void MinidumpUnloadedModuleWriter::InitializeFromSnapshot( + const UnloadedModuleSnapshot& unloaded_module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!name_); + + SetName(unloaded_module_snapshot.Name()); + + SetImageBaseAddress(unloaded_module_snapshot.Address()); + SetImageSize(InRangeCast(unloaded_module_snapshot.Size(), + std::numeric_limits::max())); + SetTimestamp(unloaded_module_snapshot.Timestamp()); + SetChecksum(unloaded_module_snapshot.Checksum()); +} + +const MINIDUMP_UNLOADED_MODULE* +MinidumpUnloadedModuleWriter::MinidumpUnloadedModule() const { + DCHECK_EQ(state(), kStateWritable); + + return &unloaded_module_; +} + +void MinidumpUnloadedModuleWriter::SetName(const std::string& name) { + DCHECK_EQ(state(), kStateMutable); + + if (!name_) { + name_.reset(new internal::MinidumpUTF16StringWriter()); + } + name_->SetUTF8(name); +} + +void MinidumpUnloadedModuleWriter::SetTimestamp(time_t timestamp) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&unloaded_module_.TimeDateStamp, + timestamp); +} + +bool MinidumpUnloadedModuleWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(name_); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + name_->RegisterRVA(&unloaded_module_.ModuleNameRva); + + return true; +} + +size_t MinidumpUnloadedModuleWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object doesn’t directly write anything itself. Its + // MINIDUMP_UNLOADED_MODULE is written by its parent as part of a + // MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing + // themselves. + return 0; +} + +std::vector +MinidumpUnloadedModuleWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(name_); + + std::vector children(1, name_.get()); + return children; +} + +bool MinidumpUnloadedModuleWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object doesn’t directly write anything itself. Its + // MINIDUMP_UNLOADED_MODULE is written by its parent as part of a + // MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing + // themselves. + return true; +} + +MinidumpUnloadedModuleListWriter::MinidumpUnloadedModuleListWriter() + : MinidumpStreamWriter(), + unloaded_modules_(), + unloaded_module_list_base_() {} + +MinidumpUnloadedModuleListWriter::~MinidumpUnloadedModuleListWriter() { +} + +void MinidumpUnloadedModuleListWriter::InitializeFromSnapshot( + const std::vector& unloaded_module_snapshots) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(unloaded_modules_.empty()); + + for (auto unloaded_module_snapshot : unloaded_module_snapshots) { + auto unloaded_module = make_scoped_ptr(new MinidumpUnloadedModuleWriter()); + unloaded_module->InitializeFromSnapshot(unloaded_module_snapshot); + AddUnloadedModule(std::move(unloaded_module)); + } +} + +void MinidumpUnloadedModuleListWriter::AddUnloadedModule( + scoped_ptr unloaded_module) { + DCHECK_EQ(state(), kStateMutable); + + unloaded_modules_.push_back(unloaded_module.release()); +} + +bool MinidumpUnloadedModuleListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + unloaded_module_list_base_.SizeOfHeader = + sizeof(MINIDUMP_UNLOADED_MODULE_LIST); + unloaded_module_list_base_.SizeOfEntry = sizeof(MINIDUMP_UNLOADED_MODULE); + + size_t unloaded_module_count = unloaded_modules_.size(); + if (!AssignIfInRange(&unloaded_module_list_base_.NumberOfEntries, + unloaded_module_count)) { + LOG(ERROR) << "unloaded_module_count " << unloaded_module_count + << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpUnloadedModuleListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(unloaded_module_list_base_) + + unloaded_modules_.size() * sizeof(MINIDUMP_UNLOADED_MODULE); +} + +std::vector +MinidumpUnloadedModuleListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + for (MinidumpUnloadedModuleWriter* unloaded_module : unloaded_modules_) { + children.push_back(unloaded_module); + } + + return children; +} + +bool MinidumpUnloadedModuleListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &unloaded_module_list_base_; + iov.iov_len = sizeof(unloaded_module_list_base_); + std::vector iovecs(1, iov); + + for (const MinidumpUnloadedModuleWriter* unloaded_module : + unloaded_modules_) { + iov.iov_base = unloaded_module->MinidumpUnloadedModule(); + iov.iov_len = sizeof(MINIDUMP_UNLOADED_MODULE); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpUnloadedModuleListWriter::StreamType() const { + return kMinidumpStreamTypeUnloadedModuleList; +} + +} // namespace crashpad diff --git a/minidump/minidump_unloaded_module_writer.h b/minidump/minidump_unloaded_module_writer.h new file mode 100644 index 00000000..dbefd602 --- /dev/null +++ b/minidump/minidump_unloaded_module_writer.h @@ -0,0 +1,154 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_ + +#include +#include +#include + +#include +#include + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" +#include "snapshot/unloaded_module_snapshot.h" + +namespace crashpad { + +//! \brief The writer for a MINIDUMP_UNLOADED_MODULE object in a minidump file. +//! +//! Because MINIDUMP_UNLOADED_MODULE objects only appear as elements of +//! MINIDUMP_UNLOADED_MODULE_LIST objects, this class does not write any data on +//! its own. It makes its MINIDUMP_UNLOADED_MODULE data available to its +//! MinidumpUnloadedModuleListWriter parent, which writes it as part of a +//! MINIDUMP_UNLOADED_MODULE_LIST. +class MinidumpUnloadedModuleWriter final : public internal::MinidumpWritable { + public: + MinidumpUnloadedModuleWriter(); + ~MinidumpUnloadedModuleWriter() override; + + //! \brief Initializes the MINIDUMP_UNLOADED_MODULE based on \a + //! unloaded_module_snapshot. + //! + //! \param[in] unloaded_module_snapshot The unloaded module snapshot to use as + //! source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot( + const UnloadedModuleSnapshot& unloaded_module_snapshot); + + //! \brief Returns a MINIDUMP_UNLOADED_MODULE referencing this object’s data. + //! + //! This method is expected to be called by a MinidumpUnloadedModuleListWriter + //! in order to obtain a MINIDUMP_UNLOADED_MODULE to include in its list. + //! + //! \note Valid in #kStateWritable. + const MINIDUMP_UNLOADED_MODULE* MinidumpUnloadedModule() const; + + //! \brief Arranges for MINIDUMP_UNLOADED_MODULE::ModuleNameRva to point to a + //! MINIDUMP_STRING containing \a name. + //! + //! \note Valid in #kStateMutable. + void SetName(const std::string& name); + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::BaseOfImage. + void SetImageBaseAddress(uint64_t image_base_address) { + unloaded_module_.BaseOfImage = image_base_address; + } + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::SizeOfImage. + void SetImageSize(uint32_t image_size) { + unloaded_module_.SizeOfImage = image_size; + } + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::CheckSum. + void SetChecksum(uint32_t checksum) { unloaded_module_.CheckSum = checksum; } + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::TimeDateStamp. + //! + //! \note Valid in #kStateMutable. + void SetTimestamp(time_t timestamp); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MINIDUMP_UNLOADED_MODULE unloaded_module_; + scoped_ptr name_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpUnloadedModuleWriter); +}; + +//! \brief The writer for a MINIDUMP_UNLOADED_MODULE_LIST stream in a minidump +//! file, containing a list of MINIDUMP_UNLOADED_MODULE objects. +class MinidumpUnloadedModuleListWriter final + : public internal::MinidumpStreamWriter { + public: + MinidumpUnloadedModuleListWriter(); + ~MinidumpUnloadedModuleListWriter() override; + + //! \brief Adds an initialized MINIDUMP_UNLOADED_MODULE for each unloaded + //! module in \a unloaded_module_snapshots to the + //! MINIDUMP_UNLOADED_MODULE_LIST. + //! + //! \param[in] unloaded_module_snapshots The unloaded module snapshots to use + //! as source data. + //! + //! \note Valid in #kStateMutable. AddUnloadedModule() may not be called + //! before this this method, and it is not normally necessary to call + //! AddUnloadedModule() after this method. + void InitializeFromSnapshot( + const std::vector& unloaded_module_snapshots); + + //! \brief Adds a MinidumpUnloadedModuleWriter to the + //! MINIDUMP_UNLOADED_MODULE_LIST. + //! + //! This object takes ownership of \a unloaded_module and becomes its parent + //! in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddUnloadedModule( + scoped_ptr unloaded_module); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + PointerVector unloaded_modules_; + MINIDUMP_UNLOADED_MODULE_LIST unloaded_module_list_base_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpUnloadedModuleListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_ diff --git a/minidump/minidump_unloaded_module_writer_test.cc b/minidump/minidump_unloaded_module_writer_test.cc new file mode 100644 index 00000000..9032399e --- /dev/null +++ b/minidump/minidump_unloaded_module_writer_test.cc @@ -0,0 +1,162 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "minidump/minidump_unloaded_module_writer.h" + +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +void ExpectUnloadedModule(const MINIDUMP_UNLOADED_MODULE* expected, + const MINIDUMP_UNLOADED_MODULE* observed, + const std::string& file_contents, + const std::string& expected_module_name) { + EXPECT_EQ(expected->BaseOfImage, observed->BaseOfImage); + EXPECT_EQ(expected->SizeOfImage, observed->SizeOfImage); + EXPECT_EQ(expected->CheckSum, observed->CheckSum); + EXPECT_EQ(expected->TimeDateStamp, observed->TimeDateStamp); + EXPECT_NE(0u, observed->ModuleNameRva); + base::string16 observed_module_name_utf16 = + MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva); + base::string16 expected_module_name_utf16 = + base::UTF8ToUTF16(expected_module_name); + EXPECT_EQ(expected_module_name_utf16, observed_module_name_utf16); +} + +void GetUnloadedModuleListStream( + const std::string& file_contents, + const MINIDUMP_UNLOADED_MODULE_LIST** unloaded_module_list) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kUnloadedModuleListStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + const size_t kUnloadedModulesOffset = + kUnloadedModuleListStreamOffset + sizeof(MINIDUMP_UNLOADED_MODULE_LIST); + + ASSERT_GE(file_contents.size(), kUnloadedModulesOffset); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(kMinidumpStreamTypeUnloadedModuleList, directory[0].StreamType); + EXPECT_EQ(kUnloadedModuleListStreamOffset, directory[0].Location.Rva); + + *unloaded_module_list = + MinidumpWritableAtLocationDescriptor( + file_contents, directory[0].Location); + ASSERT_TRUE(unloaded_module_list); +} + +TEST(MinidumpUnloadedModuleWriter, EmptyModule) { + MinidumpFileWriter minidump_file_writer; + auto unloaded_module_list_writer = + make_scoped_ptr(new MinidumpUnloadedModuleListWriter()); + + const char kModuleName[] = "test_dll"; + + auto unloaded_module_writer = + make_scoped_ptr(new MinidumpUnloadedModuleWriter()); + unloaded_module_writer->SetName(kModuleName); + + unloaded_module_list_writer->AddUnloadedModule( + std::move(unloaded_module_writer)); + minidump_file_writer.AddStream(std::move(unloaded_module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_UNLOADED_MODULE_LIST) + + 1 * sizeof(MINIDUMP_UNLOADED_MODULE)); + + const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list)); + + EXPECT_EQ(1u, unloaded_module_list->NumberOfEntries); + + MINIDUMP_UNLOADED_MODULE expected = {}; + ASSERT_NO_FATAL_FAILURE( + ExpectUnloadedModule(&expected, + reinterpret_cast( + &unloaded_module_list[1]), + string_file.string(), + kModuleName)); +} + +TEST(MinidumpUnloadedModuleWriter, OneModule) { + MinidumpFileWriter minidump_file_writer; + auto unloaded_module_list_writer = + make_scoped_ptr(new MinidumpUnloadedModuleListWriter()); + + const char kModuleName[] = "statically_linked"; + const uint64_t kModuleBase = 0x10da69000; + const uint32_t kModuleSize = 0x1000; + const uint32_t kChecksum = 0x76543210; + const time_t kTimestamp = 0x386d4380; + + auto unloaded_module_writer = + make_scoped_ptr(new MinidumpUnloadedModuleWriter()); + unloaded_module_writer->SetName(kModuleName); + unloaded_module_writer->SetImageBaseAddress(kModuleBase); + unloaded_module_writer->SetImageSize(kModuleSize); + unloaded_module_writer->SetChecksum(kChecksum); + unloaded_module_writer->SetTimestamp(kTimestamp); + + unloaded_module_list_writer->AddUnloadedModule( + std::move(unloaded_module_writer)); + minidump_file_writer.AddStream(std::move(unloaded_module_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_UNLOADED_MODULE_LIST) + + 1 * sizeof(MINIDUMP_UNLOADED_MODULE)); + + const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list)); + + EXPECT_EQ(1u, unloaded_module_list->NumberOfEntries); + + MINIDUMP_UNLOADED_MODULE expected = {}; + expected.BaseOfImage = kModuleBase; + expected.SizeOfImage = kModuleSize; + expected.CheckSum = kChecksum; + expected.TimeDateStamp = kTimestamp; + + ASSERT_NO_FATAL_FAILURE( + ExpectUnloadedModule(&expected, + reinterpret_cast( + &unloaded_module_list[1]), + string_file.string(), + kModuleName)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/minidump/test/minidump_writable_test_util.cc b/minidump/test/minidump_writable_test_util.cc index c8ea4f44..66c0591d 100644 --- a/minidump/test/minidump_writable_test_util.cc +++ b/minidump/test/minidump_writable_test_util.cc @@ -173,6 +173,14 @@ struct MinidumpModuleListTraits { } }; +struct MinidumpUnloadedModuleListTraits { + using ListType = MINIDUMP_UNLOADED_MODULE_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_UNLOADED_MODULE) }; + static size_t ElementCount(const ListType* list) { + return list->NumberOfEntries; + } +}; + struct MinidumpThreadListTraits { using ListType = MINIDUMP_THREAD_LIST; enum : size_t { kElementSize = sizeof(MINIDUMP_THREAD) }; @@ -252,6 +260,15 @@ const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor< file_contents, location); } +template <> +const MINIDUMP_UNLOADED_MODULE_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + template <> const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor< MINIDUMP_THREAD_LIST>(const std::string& file_contents, diff --git a/minidump/test/minidump_writable_test_util.h b/minidump/test/minidump_writable_test_util.h index ea3ec003..dd8f5531 100644 --- a/minidump/test/minidump_writable_test_util.h +++ b/minidump/test/minidump_writable_test_util.h @@ -89,6 +89,7 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY); // variable-sized lists. MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_UNLOADED_MODULE_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_HANDLE_DATA_STREAM); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST); @@ -186,6 +187,12 @@ const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor< MINIDUMP_MODULE_LIST>(const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location); +template <> +const MINIDUMP_UNLOADED_MODULE_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + template <> const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor< MINIDUMP_THREAD_LIST>(const std::string& file_contents, diff --git a/snapshot/mac/process_snapshot_mac.cc b/snapshot/mac/process_snapshot_mac.cc index 3d44e935..5794b8bc 100644 --- a/snapshot/mac/process_snapshot_mac.cc +++ b/snapshot/mac/process_snapshot_mac.cc @@ -186,6 +186,12 @@ std::vector ProcessSnapshotMac::Modules() const { return modules; } +std::vector ProcessSnapshotMac::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + const ExceptionSnapshot* ProcessSnapshotMac::Exception() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return exception_.get(); diff --git a/snapshot/mac/process_snapshot_mac.h b/snapshot/mac/process_snapshot_mac.h index 92c73763..05ea329a 100644 --- a/snapshot/mac/process_snapshot_mac.h +++ b/snapshot/mac/process_snapshot_mac.h @@ -38,6 +38,7 @@ #include "snapshot/process_snapshot.h" #include "snapshot/system_snapshot.h" #include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" #include "util/mach/mach_extensions.h" #include "util/misc/initialization_state_dcheck.h" #include "util/misc/uuid.h" @@ -126,6 +127,7 @@ class ProcessSnapshotMac final : public ProcessSnapshot { const SystemSnapshot* System() const override; std::vector Threads() const override; std::vector Modules() const override; + std::vector UnloadedModules() const override; const ExceptionSnapshot* Exception() const override; std::vector MemoryMap() const override; std::vector Handles() const override; diff --git a/snapshot/minidump/process_snapshot_minidump.cc b/snapshot/minidump/process_snapshot_minidump.cc index 6a1572a8..d2b5984c 100644 --- a/snapshot/minidump/process_snapshot_minidump.cc +++ b/snapshot/minidump/process_snapshot_minidump.cc @@ -28,6 +28,7 @@ ProcessSnapshotMinidump::ProcessSnapshotMinidump() stream_directory_(), stream_map_(), modules_(), + unloaded_modules_(), crashpad_info_(), annotations_simple_map_(), file_reader_(nullptr), @@ -171,6 +172,13 @@ std::vector ProcessSnapshotMinidump::Modules() const { return modules; } +std::vector ProcessSnapshotMinidump::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return unloaded_modules_; +} + const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // https://crashpad.chromium.org/bug/10 diff --git a/snapshot/minidump/process_snapshot_minidump.h b/snapshot/minidump/process_snapshot_minidump.h index ad1af3e8..2f6bf54c 100644 --- a/snapshot/minidump/process_snapshot_minidump.h +++ b/snapshot/minidump/process_snapshot_minidump.h @@ -33,6 +33,7 @@ #include "snapshot/process_snapshot.h" #include "snapshot/system_snapshot.h" #include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" #include "util/file/file_reader.h" #include "util/misc/initialization_state_dcheck.h" #include "util/misc/uuid.h" @@ -69,6 +70,7 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot { const SystemSnapshot* System() const override; std::vector Threads() const override; std::vector Modules() const override; + std::vector UnloadedModules() const override; const ExceptionSnapshot* Exception() const override; std::vector MemoryMap() const override; std::vector Handles() const override; @@ -94,6 +96,7 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot { std::vector stream_directory_; std::map stream_map_; PointerVector modules_; + std::vector unloaded_modules_; MinidumpCrashpadInfo crashpad_info_; std::map annotations_simple_map_; FileReaderInterface* file_reader_; // weak diff --git a/snapshot/process_snapshot.h b/snapshot/process_snapshot.h index 19d34607..8d5520cd 100644 --- a/snapshot/process_snapshot.h +++ b/snapshot/process_snapshot.h @@ -33,6 +33,7 @@ class MemorySnapshot; class ModuleSnapshot; class SystemSnapshot; class ThreadSnapshot; +class UnloadedModuleSnapshot; //! \brief An abstract interface to a snapshot representing the state of a //! process. @@ -146,6 +147,12 @@ class ProcessSnapshot { //! ProcessSnapshot object that they were obtained from. virtual std::vector Modules() const = 0; + //! \brief Returns UnloadedModuleSnapshot objects reflecting the code modules + //! the were recorded as unloaded at the time of the snapshot. + //! + //! \return A vector of UnloadedModuleSnapshot objects. + virtual std::vector UnloadedModules() const = 0; + //! \brief Returns ThreadSnapshot objects reflecting the threads (lightweight //! processes) existing in the snapshot process at the time of the //! snapshot. diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index d0011528..5020073e 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -91,6 +91,8 @@ 'process_snapshot.h', 'system_snapshot.h', 'thread_snapshot.h', + 'unloaded_module_snapshot.cc', + 'unloaded_module_snapshot.h', 'win/cpu_context_win.cc', 'win/cpu_context_win.h', 'win/exception_snapshot_win.cc', diff --git a/snapshot/test/test_process_snapshot.cc b/snapshot/test/test_process_snapshot.cc index c022dc41..74f9fe35 100644 --- a/snapshot/test/test_process_snapshot.cc +++ b/snapshot/test/test_process_snapshot.cc @@ -96,6 +96,11 @@ std::vector TestProcessSnapshot::Modules() const { return modules; } +std::vector TestProcessSnapshot::UnloadedModules() + const { + return unloaded_modules_; +} + const ExceptionSnapshot* TestProcessSnapshot::Exception() const { return exception_.get(); } diff --git a/snapshot/test/test_process_snapshot.h b/snapshot/test/test_process_snapshot.h index f771944e..f47b856d 100644 --- a/snapshot/test/test_process_snapshot.h +++ b/snapshot/test/test_process_snapshot.h @@ -33,6 +33,7 @@ #include "snapshot/process_snapshot.h" #include "snapshot/system_snapshot.h" #include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" #include "util/misc/uuid.h" #include "util/stdlib/pointer_container.h" @@ -92,6 +93,15 @@ class TestProcessSnapshot final : public ProcessSnapshot { modules_.push_back(module.release()); } + //! \brief Adds an unloaded module snapshot to be returned by + //! UnloadedModules(). + //! + //! \param[in] unloaded_module The unloaded module snapshot that will be + //! included in UnloadedModules(). + void AddModule(const UnloadedModuleSnapshot& unloaded_module) { + unloaded_modules_.push_back(unloaded_module); + } + //! \brief Sets the exception snapshot to be returned by Exception(). //! //! \param[in] exception The exception snapshot that Exception() will return. @@ -139,6 +149,7 @@ class TestProcessSnapshot final : public ProcessSnapshot { const SystemSnapshot* System() const override; std::vector Threads() const override; std::vector Modules() const override; + std::vector UnloadedModules() const override; const ExceptionSnapshot* Exception() const override; std::vector MemoryMap() const override; std::vector Handles() const override; @@ -157,6 +168,7 @@ class TestProcessSnapshot final : public ProcessSnapshot { scoped_ptr system_; PointerVector threads_; PointerVector modules_; + std::vector unloaded_modules_; scoped_ptr exception_; PointerVector memory_map_; std::vector handles_; diff --git a/snapshot/unloaded_module_snapshot.cc b/snapshot/unloaded_module_snapshot.cc new file mode 100644 index 00000000..45f606a6 --- /dev/null +++ b/snapshot/unloaded_module_snapshot.cc @@ -0,0 +1,33 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/unloaded_module_snapshot.h" + +namespace crashpad { + +UnloadedModuleSnapshot::UnloadedModuleSnapshot(uint64_t address, + uint64_t size, + uint32_t checksum, + uint32_t timestamp, + const std::string& name) + : name_(name), + address_(address), + size_(size), + checksum_(checksum), + timestamp_(timestamp) {} + +UnloadedModuleSnapshot::~UnloadedModuleSnapshot() { +} + +} // namespace crashpad diff --git a/snapshot/unloaded_module_snapshot.h b/snapshot/unloaded_module_snapshot.h new file mode 100644 index 00000000..03e100e1 --- /dev/null +++ b/snapshot/unloaded_module_snapshot.h @@ -0,0 +1,61 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_ + +#include + +#include + +namespace crashpad { + +//! \brief Information about an unloaded module that was previously loaded into +//! a snapshot process. +class UnloadedModuleSnapshot { + public: + UnloadedModuleSnapshot(uint64_t address, + uint64_t size, + uint32_t checksum, + uint32_t timestamp, + const std::string& name); + ~UnloadedModuleSnapshot(); + + //! \brief The base address of the module in the target processes' address + //! space. + uint64_t Address() const { return address_; } + + //! \brief The size of the module. + uint64_t Size() const { return size_; } + + //! \brief The checksum of the image. + uint32_t Checksum() const { return checksum_; } + + //! \brief The time and date stamp in `time_t` format. + uint32_t Timestamp() const { return timestamp_; } + + //! \brief The name of the module. + std::string Name() const { return name_; } + + private: + std::string name_; + uint64_t address_; + uint64_t size_; + uint32_t checksum_; + uint32_t timestamp_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_ diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index bf4f3034..d4f2da02 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -235,6 +235,11 @@ def RunTests(cdb_path, out.Check(r'Event\s+\d+', 'capture some event handles') out.Check(r'File\s+\d+', 'capture some file handles') + out = CdbRun(cdb_path, dump_path, 'lm') + out.Check(r'Unloaded modules:', 'captured some unloaded modules') + out.Check(r'lz32\.dll', 'found expected unloaded module lz32') + out.Check(r'wmerror\.dll', 'found expected unloaded module wmerror') + out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2') out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved') out.Check(r'ntdll\.dll', 'ntdll present', re.IGNORECASE) diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 535c9a73..9c72855f 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -21,6 +21,7 @@ #include "base/strings/utf_string_conversions.h" #include "snapshot/win/memory_snapshot_win.h" #include "snapshot/win/module_snapshot_win.h" +#include "util/win/nt_internals.h" #include "util/win/registration_protocol_win.h" #include "util/win/time.h" @@ -67,6 +68,7 @@ bool ProcessSnapshotWin::Initialize( } InitializeModules(); + InitializeUnloadedModules(); GetCrashpadOptionsInternal(&options_); @@ -177,6 +179,12 @@ std::vector ProcessSnapshotWin::Modules() const { return modules; } +std::vector ProcessSnapshotWin::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return unloaded_modules_; +} + const ExceptionSnapshot* ProcessSnapshotWin::Exception() const { return exception_.get(); } @@ -242,6 +250,53 @@ void ProcessSnapshotWin::InitializeModules() { } } +void ProcessSnapshotWin::InitializeUnloadedModules() { + // As documented by https://msdn.microsoft.com/en-us/library/cc678403.aspx + // we can retrieve the location for our unload events, and use that address in + // the target process. Unfortunately, this of course only works for + // 64-reading-64 and 32-reading-32, so at the moment, we simply do not + // retrieve unloaded modules for 64-reading-32. See + // https://crashpad.chromium.org/bug/89. + +#if defined(ARCH_CPU_X86_64) + if (!process_reader_.Is64Bit()) { + LOG(ERROR) + << "reading unloaded modules across bitness not currently supported"; + return; + } + using Traits = process_types::internal::Traits64; +#elif defined(ARCH_CPU_X86) + using Traits = process_types::internal::Traits32; +#else +#error port +#endif + + RTL_UNLOAD_EVENT_TRACE* unload_event_trace_address = + RtlGetUnloadEventTrace(); + WinVMAddress address_in_target_process = + reinterpret_cast(unload_event_trace_address); + + std::vector> events( + RTL_UNLOAD_EVENT_TRACE_NUMBER); + if (!process_reader_.ReadMemory(address_in_target_process, + events.size() * sizeof(events[0]), + &events[0])) { + return; + } + + for (const RTL_UNLOAD_EVENT_TRACE& uet : events) { + if (uet.ImageName[0]) { + unloaded_modules_.push_back(UnloadedModuleSnapshot( + uet.BaseAddress, + uet.SizeOfImage, + uet.CheckSum, + uet.TimeDateStamp, + base::UTF16ToUTF8( + base::StringPiece16(uet.ImageName, arraysize(uet.ImageName))))); + } + } +} + void ProcessSnapshotWin::GetCrashpadOptionsInternal( CrashpadInfoClientOptions* options) { CrashpadInfoClientOptions local_options; diff --git a/snapshot/win/process_snapshot_win.h b/snapshot/win/process_snapshot_win.h index c2307bee..4d33d7be 100644 --- a/snapshot/win/process_snapshot_win.h +++ b/snapshot/win/process_snapshot_win.h @@ -34,6 +34,7 @@ #include "snapshot/process_snapshot.h" #include "snapshot/system_snapshot.h" #include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" #include "snapshot/win/exception_snapshot_win.h" #include "snapshot/win/memory_map_region_snapshot_win.h" #include "snapshot/win/memory_snapshot_win.h" @@ -134,6 +135,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot { const SystemSnapshot* System() const override; std::vector Threads() const override; std::vector Modules() const override; + std::vector UnloadedModules() const override; const ExceptionSnapshot* Exception() const override; std::vector MemoryMap() const override; std::vector Handles() const override; @@ -146,6 +148,9 @@ class ProcessSnapshotWin final : public ProcessSnapshot { // Initializes modules_ on behalf of Initialize(). void InitializeModules(); + // Initializes unloaded_modules_ on behalf of Initialize(). + void InitializeUnloadedModules(); + // Initializes options_ on behalf of Initialize(). void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options); @@ -182,6 +187,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot { PointerVector extra_memory_; PointerVector threads_; PointerVector modules_; + std::vector unloaded_modules_; scoped_ptr exception_; PointerVector memory_map_; ProcessReaderWin process_reader_; diff --git a/util/win/nt_internals.cc b/util/win/nt_internals.cc index 46192b35..6fc0999d 100644 --- a/util/win/nt_internals.cc +++ b/util/win/nt_internals.cc @@ -26,6 +26,8 @@ NTSTATUS NTAPI NtOpenThread(HANDLE* ThreadHandle, OBJECT_ATTRIBUTES* ObjectAttributes, CLIENT_ID* ClientId); +void* NTAPI RtlGetUnloadEventTrace(); + namespace crashpad { NTSTATUS NtQuerySystemInformation( @@ -83,6 +85,14 @@ NTSTATUS NtQueryObject(HANDLE handle, return_length); } +template +RTL_UNLOAD_EVENT_TRACE* RtlGetUnloadEventTrace() { + static const auto rtl_get_unload_event_trace = + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlGetUnloadEventTrace); + return reinterpret_cast*>( + rtl_get_unload_event_trace()); +} + // Explicit instantiations with the only 2 valid template arguments to avoid // putting the body of the function in the header. template NTSTATUS NtOpenThread( @@ -99,4 +109,10 @@ template NTSTATUS NtOpenThread( const process_types::CLIENT_ID* client_id); +template RTL_UNLOAD_EVENT_TRACE* +RtlGetUnloadEventTrace(); + +template RTL_UNLOAD_EVENT_TRACE* +RtlGetUnloadEventTrace(); + } // namespace crashpad diff --git a/util/win/nt_internals.h b/util/win/nt_internals.h index aab884f5..b6221018 100644 --- a/util/win/nt_internals.h +++ b/util/win/nt_internals.h @@ -54,4 +54,26 @@ NTSTATUS NtQueryObject(HANDLE handle, ULONG object_information_length, ULONG* return_length); +// From https://msdn.microsoft.com/en-us/library/bb432428(VS.85).aspx and +// http://processhacker.sourceforge.net/doc/struct___r_t_l___u_n_l_o_a_d___e_v_e_n_t___t_r_a_c_e.html +#define RTL_UNLOAD_EVENT_TRACE_NUMBER 64 + +template +struct RTL_UNLOAD_EVENT_TRACE { + typename Traits::Pointer BaseAddress; + typename Traits::UnsignedIntegral SizeOfImage; + ULONG Sequence; + ULONG TimeDateStamp; + ULONG CheckSum; + WCHAR ImageName[32]; + ULONG Version0; + union { + ULONG Version1; + typename Traits::Pad alignment_for_x64; + }; +}; + +template +RTL_UNLOAD_EVENT_TRACE* RtlGetUnloadEventTrace(); + } // namespace crashpad From 7217cc0a8f26e1301d0756b9e452a6c08d72b531 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 26 Feb 2016 14:42:47 -0800 Subject: [PATCH 12/16] Support client-specified extra memory ranges Change-Id: I378e2513a4894fb1548445b660bb3db86e281572 Reviewed-on: https://chromium-review.googlesource.com/329564 Reviewed-by: Robert Sesek Reviewed-by: Mark Mentovai --- client/client.gyp | 2 + client/client_test.gyp | 1 + client/crashpad_info.cc | 2 +- client/crashpad_info.h | 18 ++ client/simple_address_range_bag.cc | 45 ++++ client/simple_address_range_bag.h | 193 ++++++++++++++++++ client/simple_address_range_bag_test.cc | 109 ++++++++++ client/simple_string_dictionary.h | 27 --- client/simple_string_dictionary_test.cc | 28 --- handler/win/crashy_test_program.cc | 38 ++++ snapshot/mac/module_snapshot_mac.cc | 5 + snapshot/mac/module_snapshot_mac.h | 1 + snapshot/minidump/module_snapshot_minidump.cc | 6 + snapshot/minidump/module_snapshot_minidump.h | 1 + snapshot/module_snapshot.h | 6 + snapshot/snapshot_test.gyp | 13 ++ snapshot/test/test_module_snapshot.cc | 7 +- snapshot/test/test_module_snapshot.h | 6 + ...shpad_snapshot_test_extra_memory_ranges.cc | 54 +++++ snapshot/win/end_to_end_test.py | 22 ++ snapshot/win/extra_memory_ranges_test.cc | 133 ++++++++++++ snapshot/win/module_snapshot_win.cc | 40 ++++ snapshot/win/module_snapshot_win.h | 5 + snapshot/win/pe_image_reader.h | 1 + snapshot/win/process_snapshot_win.cc | 9 +- util/numeric/checked_range.h | 5 + 26 files changed, 719 insertions(+), 58 deletions(-) create mode 100644 client/simple_address_range_bag.cc create mode 100644 client/simple_address_range_bag.h create mode 100644 client/simple_address_range_bag_test.cc create mode 100644 snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc create mode 100644 snapshot/win/extra_memory_ranges_test.cc diff --git a/client/client.gyp b/client/client.gyp index 3524be41..9892fb6c 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -46,6 +46,8 @@ 'settings.h', 'simple_string_dictionary.cc', 'simple_string_dictionary.h', + 'simple_address_range_bag.cc', + 'simple_address_range_bag.h', 'simulate_crash.h', 'simulate_crash_mac.cc', 'simulate_crash_mac.h', diff --git a/client/client_test.gyp b/client/client_test.gyp index 80cddc84..66ec59ea 100644 --- a/client/client_test.gyp +++ b/client/client_test.gyp @@ -39,6 +39,7 @@ 'crashpad_client_win_test.cc', 'prune_crash_reports_test.cc', 'settings_test.cc', + 'simple_address_range_bag_test.cc', 'simple_string_dictionary_test.cc', 'simulate_crash_mac_test.cc', ], diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 46e73210..5913be81 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -100,13 +100,13 @@ CrashpadInfo::CrashpadInfo() system_crash_reporter_forwarding_(TriState::kUnset), gather_indirectly_referenced_memory_(TriState::kUnset), padding_0_(0), + extra_memory_ranges_(nullptr), simple_annotations_(nullptr) #if !defined(NDEBUG) && defined(OS_WIN) , invalid_read_detection_(0xbadc0de) #endif { - } } // namespace crashpad diff --git a/client/crashpad_info.h b/client/crashpad_info.h index 377d194e..4dd61abe 100644 --- a/client/crashpad_info.h +++ b/client/crashpad_info.h @@ -19,6 +19,7 @@ #include "base/macros.h" #include "build/build_config.h" +#include "client/simple_address_range_bag.h" #include "client/simple_string_dictionary.h" #include "util/misc/tri_state.h" @@ -42,6 +43,22 @@ struct CrashpadInfo { CrashpadInfo(); + //! \brief Sets the bag of extra memory ranges to be included in the snapshot. + //! + //! Extra memory ranges may exist in \a address_range_bag at the time that + //! this method is called, or they may be added, removed, or modified in \a + //! address_range_bag after this method is called. + //! + //! TODO(scottmg) This is currently only supported on Windows. + //! + //! \param[in] address_range_bag A bag of address ranges. The CrashpadInfo + //! object does not take ownership of the SimpleAddressRangeBag object. + //! It is the caller’s responsibility to ensure that this pointer remains + //! valid while it is in effect for a CrashpadInfo object. + void set_extra_memory_ranges(SimpleAddressRangeBag* address_range_bag) { + extra_memory_ranges_ = address_range_bag; + } + //! \brief Sets the simple annotations dictionary. //! //! Simple annotations set on a CrashpadInfo structure are interpreted by @@ -135,6 +152,7 @@ struct CrashpadInfo { TriState system_crash_reporter_forwarding_; TriState gather_indirectly_referenced_memory_; uint8_t padding_0_; + SimpleAddressRangeBag* extra_memory_ranges_; // weak SimpleStringDictionary* simple_annotations_; // weak #if !defined(NDEBUG) && defined(OS_WIN) diff --git a/client/simple_address_range_bag.cc b/client/simple_address_range_bag.cc new file mode 100644 index 00000000..50ecfbc4 --- /dev/null +++ b/client/simple_address_range_bag.cc @@ -0,0 +1,45 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "client/simple_address_range_bag.h" + +#include "util/stdlib/cxx.h" + +#if CXX_LIBRARY_VERSION >= 2011 +#include +#endif + +namespace crashpad { +namespace { + +using SimpleAddressRangeBagForAssertion = TSimpleAddressRangeBag<1>; + +#if CXX_LIBRARY_VERSION >= 2011 +// In C++11, check that TSimpleAddressRangeBag has standard layout, which is +// what is actually important. +static_assert( + std::is_standard_layout::value, + "SimpleAddressRangeBag must be standard layout"); +#else +// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member +// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this +// property to ensure that Entry remains POD. This doesn’t work for C++11 +// because the requirements for unions have been relaxed. +union Compile_Assert { + SimpleAddressRangeBagForAssertion::Entry Compile_Assert__entry_must_be_pod; +}; +#endif + +} // namespace +} // namespace crashpad diff --git a/client/simple_address_range_bag.h b/client/simple_address_range_bag.h new file mode 100644 index 00000000..336a748d --- /dev/null +++ b/client/simple_address_range_bag.h @@ -0,0 +1,193 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_ +#define CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_ + +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { + +//! \brief A bag implementation using a fixed amount of storage, so that it does +//! not perform any dynamic allocations for its operations. +//! +//! The actual bag storage (TSimpleAddressRangeBag::Entry) is POD, so that it +//! can be transmitted over various IPC mechanisms. +template +class TSimpleAddressRangeBag { + public: + //! Constant and publicly accessible version of the template parameter. + static const size_t num_entries = NumEntries; + + //! \brief A single entry in the bag. + struct Entry { + //! \brief The base address of the range. + uint64_t base; + + //! \brief The size of the range in bytes. + uint64_t size; + + //! \brief Returns the validity of the entry. + //! + //! If #base and #size are both zero, the entry is considered inactive, and + //! this method returns `false`. Otherwise, returns `true`. + bool is_active() const { + return base != 0 || size != 0; + } + }; + + //! \brief An iterator to traverse all of the active entries in a + //! TSimpleAddressRangeBag. + class Iterator { + public: + explicit Iterator(const TSimpleAddressRangeBag& bag) + : bag_(bag), + current_(0) { + } + + //! \brief Returns the next entry in the bag, or `nullptr` if at the end of + //! the collection. + const Entry* Next() { + while (current_ < bag_.num_entries) { + const Entry* entry = &bag_.entries_[current_++]; + if (entry->is_active()) { + return entry; + } + } + return nullptr; + } + + private: + const TSimpleAddressRangeBag& bag_; + size_t current_; + + DISALLOW_COPY_AND_ASSIGN(Iterator); + }; + + TSimpleAddressRangeBag() + : entries_() { + } + + TSimpleAddressRangeBag(const TSimpleAddressRangeBag& other) { + *this = other; + } + + TSimpleAddressRangeBag& operator=(const TSimpleAddressRangeBag& other) { + memcpy(entries_, other.entries_, sizeof(entries_)); + return *this; + } + + //! \brief Returns the number of active entries. The upper limit for this is + //! \a NumEntries. + size_t GetCount() const { + size_t count = 0; + for (size_t i = 0; i < num_entries; ++i) { + if (entries_[i].is_active()) { + ++count; + } + } + return count; + } + + //! \brief Inserts the given range into the bag. Duplicates and overlapping + //! ranges are supported and allowed, but not coalesced. + //! + //! \param[in] range The range to be inserted. The range must have either a + //! non-zero base address or size. + //! + //! \return `true` if there was space to insert the range into the bag, + //! otherwise `false` with an error logged. + bool Insert(CheckedRange range) { + DCHECK(range.base() != 0 || range.size() != 0); + + for (size_t i = 0; i < num_entries; ++i) { + if (!entries_[i].is_active()) { + entries_[i].base = range.base(); + entries_[i].size = range.size(); + return true; + } + } + + LOG(ERROR) << "no space available to insert range"; + return false; + } + + //! \brief Inserts the given range into the bag. Duplicates and overlapping + //! ranges are supported and allowed, but not coalesced. + //! + //! \param[in] base The base of the range to be inserted. May not be null. + //! \param[in] size The size of the range to be inserted. May not be zero. + //! + //! \return `true` if there was space to insert the range into the bag, + //! otherwise `false` with an error logged. + bool Insert(void* base, size_t size) { + DCHECK(base != nullptr); + DCHECK_NE(0u, size); + return Insert(CheckedRange( + base::checked_cast(reinterpret_cast(base)), + base::checked_cast(size))); + } + + //! \brief Removes the given range from the bag. + //! + //! \param[in] range The range to be removed. The range must have either a + //! non-zero base address or size. + //! + //! \return `true` if the range was found and removed, otherwise `false` with + //! an error logged. + bool Remove(CheckedRange range) { + DCHECK(range.base() != 0 || range.size() != 0); + + for (size_t i = 0; i < num_entries; ++i) { + if (entries_[i].base == range.base() && + entries_[i].size == range.size()) { + entries_[i].base = entries_[i].size = 0; + return true; + } + } + + LOG(ERROR) << "did not find range to remove"; + return false; + } + + //! \brief Removes the given range from the bag. + //! + //! \param[in] base The base of the range to be removed. May not be null. + //! \param[in] size The size of the range to be removed. May not be zero. + //! + //! \return `true` if the range was found and removed, otherwise `false` with + //! an error logged. + bool Remove(void* base, size_t size) { + DCHECK(base != nullptr); + DCHECK_NE(0u, size); + return Remove(CheckedRange( + base::checked_cast(reinterpret_cast(base)), + base::checked_cast(size))); + } + + + private: + Entry entries_[NumEntries]; +}; + +//! \brief A TSimpleAddressRangeBag with default template parameters. +using SimpleAddressRangeBag = TSimpleAddressRangeBag<64>; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_ diff --git a/client/simple_address_range_bag_test.cc b/client/simple_address_range_bag_test.cc new file mode 100644 index 00000000..53cca5f0 --- /dev/null +++ b/client/simple_address_range_bag_test.cc @@ -0,0 +1,109 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "client/simple_address_range_bag.h" + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "test/gtest_death_check.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(SimpleAddressRangeBag, Entry) { + using TestBag = TSimpleAddressRangeBag<15>; + TestBag bag; + + const TestBag::Entry* entry = TestBag::Iterator(bag).Next(); + EXPECT_FALSE(entry); + + bag.Insert(reinterpret_cast(0x1000), 200); + entry = TestBag::Iterator(bag).Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(entry->base, 0x1000); + EXPECT_EQ(entry->size, 200); + + bag.Remove(reinterpret_cast(0x1000), 200); + EXPECT_FALSE(entry->is_active()); + EXPECT_EQ(entry->base, 0); + EXPECT_EQ(entry->size, 0); +} + +TEST(SimpleAddressRangeBag, SimpleAddressRangeBag) { + SimpleAddressRangeBag bag; + + EXPECT_TRUE(bag.Insert(reinterpret_cast(0x1000), 10)); + EXPECT_TRUE(bag.Insert(reinterpret_cast(0x2000), 20)); + EXPECT_TRUE(bag.Insert(CheckedRange(0x3000, 30))); + + EXPECT_EQ(bag.GetCount(), 3u); + + // Duplicates added too. + EXPECT_TRUE(bag.Insert(CheckedRange(0x3000, 30))); + EXPECT_TRUE(bag.Insert(CheckedRange(0x3000, 30))); + EXPECT_EQ(bag.GetCount(), 5u); + + // Can be removed 3 times, but not the 4th time. + EXPECT_TRUE(bag.Remove(CheckedRange(0x3000, 30))); + EXPECT_TRUE(bag.Remove(CheckedRange(0x3000, 30))); + EXPECT_TRUE(bag.Remove(CheckedRange(0x3000, 30))); + EXPECT_EQ(bag.GetCount(), 2); + EXPECT_FALSE(bag.Remove(CheckedRange(0x3000, 30))); + EXPECT_EQ(bag.GetCount(), 2); + + EXPECT_TRUE(bag.Remove(reinterpret_cast(0x1000), 10)); + EXPECT_TRUE(bag.Remove(reinterpret_cast(0x2000), 20)); + EXPECT_EQ(bag.GetCount(), 0); +} + +TEST(SimpleAddressRangeBag, CopyAndAssign) { + TSimpleAddressRangeBag<10> bag; + EXPECT_TRUE(bag.Insert(CheckedRange(1, 2))); + EXPECT_TRUE(bag.Insert(CheckedRange(3, 4))); + EXPECT_TRUE(bag.Insert(CheckedRange(5, 6))); + EXPECT_TRUE(bag.Remove(CheckedRange(3, 4))); + EXPECT_EQ(2u, bag.GetCount()); + + // Test copy. + TSimpleAddressRangeBag<10> bag_copy(bag); + EXPECT_EQ(2u, bag_copy.GetCount()); + EXPECT_TRUE(bag_copy.Remove(CheckedRange(1, 2))); + EXPECT_TRUE(bag_copy.Remove(CheckedRange(5, 6))); + EXPECT_EQ(0u, bag_copy.GetCount()); + EXPECT_EQ(2u, bag.GetCount()); + + // Test assign. + TSimpleAddressRangeBag<10> bag_assign; + bag_assign = bag; + EXPECT_EQ(2u, bag_assign.GetCount()); + EXPECT_TRUE(bag_assign.Remove(CheckedRange(1, 2))); + EXPECT_TRUE(bag_assign.Remove(CheckedRange(5, 6))); + EXPECT_EQ(0u, bag_assign.GetCount()); + EXPECT_EQ(2u, bag.GetCount()); +} + +// Running out of space shouldn't crash. +TEST(SimpleAddressRangeBag, OutOfSpace) { + TSimpleAddressRangeBag<2> bag; + EXPECT_TRUE(bag.Insert(CheckedRange(1, 2))); + EXPECT_TRUE(bag.Insert(CheckedRange(3, 4))); + EXPECT_FALSE(bag.Insert(CheckedRange(5, 6))); + EXPECT_EQ(2u, bag.GetCount()); + EXPECT_FALSE(bag.Remove(CheckedRange(5, 6))); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/client/simple_string_dictionary.h b/client/simple_string_dictionary.h index 2fb19bba..ea142fcd 100644 --- a/client/simple_string_dictionary.h +++ b/client/simple_string_dictionary.h @@ -24,11 +24,6 @@ namespace crashpad { -// Opaque type for the serialized representation of a TSimpleStringDictionary. -// One is created in TSimpleStringDictionary::Serialize and can be deserialized -// using one of the constructors. -struct SerializedSimpleStringDictionary; - //! \brief A map/dictionary collection implementation using a fixed amount of //! storage, so that it does not perform any dynamic allocations for its //! operations. @@ -112,16 +107,6 @@ class TSimpleStringDictionary { return *this; } - //! \brief Constructs a map from its serialized form. \a map should be the out - //! parameter from Serialize(), and \a size should be its return value. - TSimpleStringDictionary( - const SerializedSimpleStringDictionary* map, size_t size) { - DCHECK_EQ(size, sizeof(entries_)); - if (size == sizeof(entries_)) { - memcpy(entries_, map, size); - } - } - //! \brief Returns the number of active key/value pairs. The upper limit for //! this is \a NumEntries. size_t GetCount() const { @@ -236,18 +221,6 @@ class TSimpleStringDictionary { DCHECK_EQ(GetEntryForKey(key), implicit_cast(nullptr)); } - //! \brief Returns a serialized form of the map. - //! - //! Places a serialized version of the map into \a map and returns the size in - //! bytes. Both \a map and the size should be passed to the deserializing - //! constructor. Note that the serialized \a map is scoped to the lifetime of - //! the non-serialized instance of this class. The \a map data can be copied - //! across IPC boundaries. - size_t Serialize(const SerializedSimpleStringDictionary** map) const { - *map = reinterpret_cast(entries_); - return sizeof(entries_); - } - private: const Entry* GetConstEntryForKey(const char* key) const { for (size_t i = 0; i < num_entries; ++i) { diff --git a/client/simple_string_dictionary_test.cc b/client/simple_string_dictionary_test.cc index 6396c2fe..69e892b4 100644 --- a/client/simple_string_dictionary_test.cc +++ b/client/simple_string_dictionary_test.cc @@ -240,34 +240,6 @@ TEST(SimpleStringDictionary, AddRemove) { EXPECT_FALSE(map.GetValueForKey("mark")); } -TEST(SimpleStringDictionary, Serialize) { - using TestMap = TSimpleStringDictionary<4, 5, 7>; - TestMap map; - map.SetKeyValue("one", "abc"); - map.SetKeyValue("two", "def"); - map.SetKeyValue("tre", "hig"); - - EXPECT_STREQ("abc", map.GetValueForKey("one")); - EXPECT_STREQ("def", map.GetValueForKey("two")); - EXPECT_STREQ("hig", map.GetValueForKey("tre")); - - const SerializedSimpleStringDictionary* serialized; - size_t size = map.Serialize(&serialized); - - SerializedSimpleStringDictionary* serialized_copy = - reinterpret_cast(malloc(size)); - ASSERT_TRUE(serialized_copy); - memcpy(serialized_copy, serialized, size); - - TestMap deserialized(serialized_copy, size); - free(serialized_copy); - - EXPECT_EQ(3u, deserialized.GetCount()); - EXPECT_STREQ("abc", deserialized.GetValueForKey("one")); - EXPECT_STREQ("def", deserialized.GetValueForKey("two")); - EXPECT_STREQ("hig", deserialized.GetValueForKey("tre")); -} - // Running out of space shouldn't crash. TEST(SimpleStringDictionary, OutOfSpace) { TSimpleStringDictionary<3, 2, 2> map; diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index 51ff0129..a3331398 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -38,6 +38,10 @@ #endif namespace crashpad { + +int* g_extra_memory_pointer; +int* g_extra_memory_not_saved; + namespace { CRITICAL_SECTION g_test_critical_section; @@ -136,6 +140,33 @@ void SomeCrashyFunction() { *foo = 42; } +void AllocateExtraMemoryToBeSaved( + crashpad::SimpleAddressRangeBag* extra_ranges) { + const size_t kNumInts = 2000; + int* extra_memory = new int[kNumInts]; + g_extra_memory_pointer = extra_memory; + for (int i = 0; i < kNumInts; ++i) + extra_memory[i] = i * 13 + 2; + extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts); + extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer)); +} + +void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) { + // Allocate some extra memory, and then Insert() but also Remove() it so we + // can confirm it doesn't get saved. + const size_t kNumInts = 2000; + int* extra_memory = new int[kNumInts]; + g_extra_memory_not_saved = extra_memory; + for (int i = 0; i < kNumInts; ++i) + extra_memory[i] = i * 17 + 7; + extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts); + extra_ranges->Insert(&g_extra_memory_not_saved, + sizeof(g_extra_memory_not_saved)); + + // We keep the pointer's memory, but remove the pointed-to memory. + extra_ranges->Remove(extra_memory, sizeof(extra_memory[0]) * kNumInts); +} + int CrashyMain(int argc, wchar_t* argv[]) { CrashpadClient client; @@ -165,6 +196,13 @@ int CrashyMain(int argc, wchar_t* argv[]) { return EXIT_FAILURE; } + crashpad::SimpleAddressRangeBag* extra_ranges = + new crashpad::SimpleAddressRangeBag(); + crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges( + extra_ranges); + AllocateExtraMemoryToBeSaved(extra_ranges); + AllocateExtraUnsavedMemory(extra_ranges); + // Load and unload some uncommonly used modules so we can see them in the list // reported by `lm`. At least two so that we confirm we got the size of // RTL_UNLOAD_EVENT_TRACE right. diff --git a/snapshot/mac/module_snapshot_mac.cc b/snapshot/mac/module_snapshot_mac.cc index dd9e8097..4f4df2a5 100644 --- a/snapshot/mac/module_snapshot_mac.cc +++ b/snapshot/mac/module_snapshot_mac.cc @@ -182,5 +182,10 @@ std::map ModuleSnapshotMac::AnnotationsSimpleMap() return annotations_reader.SimpleMap(); } +std::set> ModuleSnapshotMac::ExtraMemoryRanges() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::set>(); +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/mac/module_snapshot_mac.h b/snapshot/mac/module_snapshot_mac.h index b1a232fd..0c6bd69a 100644 --- a/snapshot/mac/module_snapshot_mac.h +++ b/snapshot/mac/module_snapshot_mac.h @@ -79,6 +79,7 @@ class ModuleSnapshotMac final : public ModuleSnapshot { std::string DebugFileName() const override; std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; + std::set> ExtraMemoryRanges() const override; private: std::string name_; diff --git a/snapshot/minidump/module_snapshot_minidump.cc b/snapshot/minidump/module_snapshot_minidump.cc index 973d3b27..c83de596 100644 --- a/snapshot/minidump/module_snapshot_minidump.cc +++ b/snapshot/minidump/module_snapshot_minidump.cc @@ -135,6 +135,12 @@ ModuleSnapshotMinidump::AnnotationsSimpleMap() const { return annotations_simple_map_; } +std::set> ModuleSnapshotMinidump::ExtraMemoryRanges() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::set>(); +} + bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo( FileReaderInterface* file_reader, const MINIDUMP_LOCATION_DESCRIPTOR* diff --git a/snapshot/minidump/module_snapshot_minidump.h b/snapshot/minidump/module_snapshot_minidump.h index b9b7d559..dc375f03 100644 --- a/snapshot/minidump/module_snapshot_minidump.h +++ b/snapshot/minidump/module_snapshot_minidump.h @@ -76,6 +76,7 @@ class ModuleSnapshotMinidump final : public ModuleSnapshot { std::string DebugFileName() const override; std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; + std::set> ExtraMemoryRanges() const override; private: // Initializes data carried in a MinidumpModuleCrashpadInfo structure on diff --git a/snapshot/module_snapshot.h b/snapshot/module_snapshot.h index a1bafd92..3a1c3216 100644 --- a/snapshot/module_snapshot.h +++ b/snapshot/module_snapshot.h @@ -19,10 +19,12 @@ #include #include +#include #include #include #include "util/misc/uuid.h" +#include "util/numeric/checked_range.h" namespace crashpad { @@ -168,6 +170,10 @@ class ModuleSnapshot { //! system, or snapshot producer may be obtained by calling //! ProcessSnapshot::AnnotationsSimpleMap(). virtual std::map AnnotationsSimpleMap() const = 0; + + //! \brief Returns a set of extra memory ranges specified in the module as + //! being desirable to include in the crash dump. + virtual std::set> ExtraMemoryRanges() const = 0; }; } // namespace crashpad diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index 9bfcf8b3..4ca9f4d5 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -80,6 +80,7 @@ 'minidump/process_snapshot_minidump_test.cc', 'win/cpu_context_win_test.cc', 'win/exception_snapshot_win_test.cc', + 'win/extra_memory_ranges_test.cc', 'win/pe_image_annotations_reader_test.cc', 'win/pe_image_reader_test.cc', 'win/process_reader_win_test.cc', @@ -170,6 +171,18 @@ 'win/crashpad_snapshot_test_dump_without_crashing.cc', ], }, + { + 'target_name': 'crashpad_snapshot_test_extra_memory_ranges', + 'type': 'executable', + 'dependencies': [ + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'sources': [ + 'win/crashpad_snapshot_test_extra_memory_ranges.cc', + ], + }, { 'target_name': 'crashpad_snapshot_test_image_reader', 'type': 'executable', diff --git a/snapshot/test/test_module_snapshot.cc b/snapshot/test/test_module_snapshot.cc index cfe9a1de..c853a780 100644 --- a/snapshot/test/test_module_snapshot.cc +++ b/snapshot/test/test_module_snapshot.cc @@ -29,7 +29,8 @@ TestModuleSnapshot::TestModuleSnapshot() uuid_(), debug_file_name_(), annotations_vector_(), - annotations_simple_map_() { + annotations_simple_map_(), + extra_memory_ranges_() { } TestModuleSnapshot::~TestModuleSnapshot() { @@ -93,5 +94,9 @@ std::map TestModuleSnapshot::AnnotationsSimpleMap() return annotations_simple_map_; } +std::set> TestModuleSnapshot::ExtraMemoryRanges() const { + return extra_memory_ranges_; +} + } // namespace test } // namespace crashpad diff --git a/snapshot/test/test_module_snapshot.h b/snapshot/test/test_module_snapshot.h index ff9bdbd5..bbe6193d 100644 --- a/snapshot/test/test_module_snapshot.h +++ b/snapshot/test/test_module_snapshot.h @@ -75,6 +75,10 @@ class TestModuleSnapshot final : public ModuleSnapshot { const std::map& annotations_simple_map) { annotations_simple_map_ = annotations_simple_map; } + void SetExtraMemoryRanges( + const std::set>& extra_memory_ranges) { + extra_memory_ranges_ = extra_memory_ranges; + } // ModuleSnapshot: @@ -95,6 +99,7 @@ class TestModuleSnapshot final : public ModuleSnapshot { std::string DebugFileName() const override; std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; + std::set> ExtraMemoryRanges() const override; private: std::string name_; @@ -109,6 +114,7 @@ class TestModuleSnapshot final : public ModuleSnapshot { std::string debug_file_name_; std::vector annotations_vector_; std::map annotations_simple_map_; + std::set> extra_memory_ranges_; DISALLOW_COPY_AND_ASSIGN(TestModuleSnapshot); }; diff --git a/snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc b/snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc new file mode 100644 index 00000000..58b145e5 --- /dev/null +++ b/snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc @@ -0,0 +1,54 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "base/logging.h" +#include "client/crashpad_info.h" +#include "util/file/file_io.h" + + +int wmain(int argc, wchar_t* argv[]) { + using namespace crashpad; + + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + + // This is "leaked" to crashpad_info. + SimpleAddressRangeBag* extra_ranges = new SimpleAddressRangeBag(); + extra_ranges->Insert(CheckedRange(0, 1)); + extra_ranges->Insert(CheckedRange(1, 0)); + extra_ranges->Insert(CheckedRange(0x1000000000ULL, 0x1000)); + extra_ranges->Insert(CheckedRange(0x2000, 0x2000000000ULL)); + extra_ranges->Insert(CheckedRange(1234, 5678)); + extra_ranges->Insert(CheckedRange(1234, 5678)); + extra_ranges->Insert(CheckedRange(1234, 5678)); + crashpad_info->set_extra_memory_ranges(extra_ranges); + + // Tell the parent that the environment has been set up. + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + char c = ' '; + CheckedWriteFile(out, &c, sizeof(c)); + + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + PCHECK(in != INVALID_HANDLE_VALUE) << "GetStdHandle"; + CheckedReadFile(in, &c, sizeof(c)); + CHECK(c == 'd' || c == ' '); + + // If 'd' we crash with a debug break, otherwise exit normally. + if (c == 'd') + __debugbreak(); + + return 0; +} diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index d4f2da02..404d6d3a 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -23,6 +23,7 @@ import sys import tempfile import time + g_temp_dirs = [] @@ -268,6 +269,27 @@ def RunTests(cdb_path, r'80000094 00000095 80000096 00000097', 'data pointed to by stack captured') + # Attempt to retrieve the value of g_extra_memory_pointer (by name), and then + # examine the memory at which it points. Both should have been saved. + out = CdbRun(cdb_path, dump_path, + 'dd poi(crashy_program!crashpad::g_extra_memory_pointer)+0x1f30 ' + 'L8') + out.Check(r'0000655e 0000656b 00006578 00006585', + 'extra memory range captured') + out.Check(r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\? ' + r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?', + ' and not memory after range') + + out = CdbRun(cdb_path, dump_path, + 'dd poi(crashy_program!crashpad::g_extra_memory_not_saved)' + '+0x1f30 L4') + # We save only the pointer, not the pointed-to data. If the pointer itself + # wasn't saved, then we won't get any memory printed, so here we're confirming + # the pointer was saved but the memory wasn't. + out.Check(r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\? ' + r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?', + 'extra memory removal') + if z7_dump_path: out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm') out.Check('This dump file has an exception of interest stored in it', diff --git a/snapshot/win/extra_memory_ranges_test.cc b/snapshot/win/extra_memory_ranges_test.cc new file mode 100644 index 00000000..b623eccf --- /dev/null +++ b/snapshot/win/extra_memory_ranges_test.cc @@ -0,0 +1,133 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/win/module_snapshot_win.h" + +#include +#include + +#include + +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "client/simple_address_range_bag.h" +#include "gtest/gtest.h" +#include "snapshot/win/process_snapshot_win.h" +#include "test/paths.h" +#include "test/win/child_launcher.h" +#include "util/file/file_io.h" +#include "util/win/process_info.h" + +namespace crashpad { +namespace test { +namespace { + +enum TestType { + // Don't crash, just test the CrashpadInfo interface. + kDontCrash = 0, + + // The child process should crash by __debugbreak(). + kCrashDebugBreak, +}; + +void TestExtraMemoryRanges(TestType type, + const base::string16& directory_modification) { + // Spawn a child process, passing it the pipe name to connect to. + base::FilePath test_executable = Paths::Executable(); + std::wstring child_test_executable = + test_executable.DirName() + .Append(directory_modification) + .Append(test_executable.BaseName().RemoveFinalExtension().value() + + L"_extra_memory_ranges.exe") + .value(); + ChildLauncher child(child_test_executable, L""); + child.Start(); + + // Wait for the child process to indicate that it's done setting up its + // annotations via the CrashpadInfo interface. + char c; + CheckedReadFile(child.stdout_read_handle(), &c, sizeof(c)); + + ProcessSnapshotWin snapshot; + ASSERT_TRUE(snapshot.Initialize( + child.process_handle(), ProcessSuspensionState::kRunning, 0)); + + // Verify the extra memory ranges set via the CrashpadInfo interface. + std::set> all_ranges; + for (const auto* module : snapshot.Modules()) { + for (const auto& range : module->ExtraMemoryRanges()) + all_ranges.insert(range); + } + + EXPECT_EQ(5u, all_ranges.size()); + EXPECT_NE(all_ranges.end(), all_ranges.find(CheckedRange(0, 1))); + EXPECT_NE(all_ranges.end(), all_ranges.find(CheckedRange(1, 0))); + EXPECT_NE(all_ranges.end(), + all_ranges.find(CheckedRange(1234, 5678))); + EXPECT_NE(all_ranges.end(), + all_ranges.find(CheckedRange(0x1000000000ULL, 0x1000))); + EXPECT_NE(all_ranges.end(), + all_ranges.find(CheckedRange(0x2000, 0x2000000000ULL))); + + // Tell the child process to continue. + DWORD expected_exit_code; + switch (type) { + case kDontCrash: + c = ' '; + expected_exit_code = 0; + break; + case kCrashDebugBreak: + c = 'd'; + expected_exit_code = STATUS_BREAKPOINT; + break; + default: + FAIL(); + } + CheckedWriteFile(child.stdin_write_handle(), &c, sizeof(c)); + + EXPECT_EQ(expected_exit_code, child.WaitForExit()); +} + +TEST(ExtraMemoryRanges, DontCrash) { + TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL(".")); +} + +TEST(ExtraMemoryRanges, CrashDebugBreak) { + TestExtraMemoryRanges(kCrashDebugBreak, FILE_PATH_LITERAL(".")); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(ExtraMemoryRanges, DontCrashWOW64) { +#ifndef NDEBUG + TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Debug")); +#else + TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Release")); +#endif +} + +TEST(ExtraMemoryRanges, CrashDebugBreakWOW64) { +#ifndef NDEBUG + TestExtraMemoryRanges(kCrashDebugBreak, + FILE_PATH_LITERAL("..\\..\\out\\Debug")); +#else + TestExtraMemoryRanges(kCrashDebugBreak, + FILE_PATH_LITERAL("..\\..\\out\\Release")); +#endif +} +#endif // ARCH_CPU_64_BITS + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index 8948b2ab..b86e2029 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -15,6 +15,7 @@ #include "snapshot/win/module_snapshot_win.h" #include "base/strings/utf_string_conversions.h" +#include "client/simple_address_range_bag.h" #include "snapshot/win/pe_image_annotations_reader.h" #include "snapshot/win/pe_image_reader.h" #include "util/misc/tri_state.h" @@ -185,6 +186,16 @@ std::map ModuleSnapshotWin::AnnotationsSimpleMap() return annotations_reader.SimpleMap(); } +std::set> ModuleSnapshotWin::ExtraMemoryRanges() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::set> ranges; + if (process_reader_->Is64Bit()) + GetCrashpadExtraMemoryRanges(&ranges); + else + GetCrashpadExtraMemoryRanges(&ranges); + return ranges; +} + template void ModuleSnapshotWin::GetCrashpadOptionsInternal( CrashpadInfoClientOptions* options) { @@ -222,5 +233,34 @@ const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const { : nullptr; } +template +void ModuleSnapshotWin::GetCrashpadExtraMemoryRanges( + std::set>* ranges) const { + process_types::CrashpadInfo crashpad_info; + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) + return; + + if (!crashpad_info.extra_address_ranges) + return; + + std::vector simple_ranges( + SimpleAddressRangeBag::num_entries); + if (!process_reader_->ReadMemory( + crashpad_info.extra_address_ranges, + simple_ranges.size() * sizeof(simple_ranges[0]), + &simple_ranges[0])) { + LOG(WARNING) << "could not read simple address_ranges from " + << base::UTF16ToUTF8(name_); + return; + } + + for (const auto& entry : simple_ranges) { + if (entry.base != 0 || entry.size != 0) { + // Deduplication here is fine. + ranges->insert(CheckedRange(entry.base, entry.size)); + } + } +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/win/module_snapshot_win.h b/snapshot/win/module_snapshot_win.h index 99327de5..85aeb123 100644 --- a/snapshot/win/module_snapshot_win.h +++ b/snapshot/win/module_snapshot_win.h @@ -85,11 +85,16 @@ class ModuleSnapshotWin final : public ModuleSnapshot { std::string DebugFileName() const override; std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; + std::set> ExtraMemoryRanges() const override; private: template void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options); + template + void GetCrashpadExtraMemoryRanges( + std::set>* ranges) const; + // Initializes vs_fixed_file_info_ if it has not yet been initialized, and // returns a pointer to it. Returns nullptr on failure, with a message logged // on the first call. diff --git a/snapshot/win/pe_image_reader.h b/snapshot/win/pe_image_reader.h index feffa8a8..96529bf8 100644 --- a/snapshot/win/pe_image_reader.h +++ b/snapshot/win/pe_image_reader.h @@ -43,6 +43,7 @@ struct CrashpadInfo { uint8_t system_crash_reporter_forwarding; // TriState. uint8_t gather_indirectly_referenced_memory; // TriState. uint8_t padding_0; + typename Traits::Pointer extra_address_ranges; typename Traits::Pointer simple_annotations; }; diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 9c72855f..41c39027 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -80,6 +80,12 @@ bool ProcessSnapshotWin::Initialize( memory_map_.push_back(new internal::MemoryMapRegionSnapshotWin(mbi)); } + for (const auto& module : modules_) { + for (const auto& range : module->ExtraMemoryRanges()) { + AddMemorySnapshot(range.base(), range.size(), &extra_memory_); + } + } + INITIALIZATION_STATE_SET_VALID(initialized_); return true; } @@ -321,7 +327,8 @@ void ProcessSnapshotWin::GetCrashpadOptionsInternal( // If non-default values have been found for all options, the loop can end // early. if (local_options.crashpad_handler_behavior != TriState::kUnset && - local_options.system_crash_reporter_forwarding != TriState::kUnset) { + local_options.system_crash_reporter_forwarding != TriState::kUnset && + local_options.gather_indirectly_referenced_memory != TriState::kUnset) { break; } } diff --git a/util/numeric/checked_range.h b/util/numeric/checked_range.h index 69fe9df8..09dfc227 100644 --- a/util/numeric/checked_range.h +++ b/util/numeric/checked_range.h @@ -16,6 +16,7 @@ #define CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_ #include +#include #include "base/logging.h" #include "base/numerics/safe_conversions.h" @@ -125,6 +126,10 @@ class CheckedRange { return base() < that.end() && that.base() < end(); } + bool operator<(const CheckedRange& other) const { + return std::tie(base_, size_) < std::tie(other.base_, other.size_); + } + private: ValueType base_; SizeType size_; From f177d77c51ff49e549915c4b92994591b29af97d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 29 Feb 2016 14:24:49 -0800 Subject: [PATCH 13/16] Fix Mac build after 7217cc0a8f Forgot some u number suffixes. BUG=crashpad:94 Change-Id: I2a3b47b4eab07bf5b9ced3859f5a8b388a840b35 Reviewed-on: https://chromium-review.googlesource.com/329760 Reviewed-by: Robert Sesek --- client/simple_address_range_bag_test.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/simple_address_range_bag_test.cc b/client/simple_address_range_bag_test.cc index 53cca5f0..0598d294 100644 --- a/client/simple_address_range_bag_test.cc +++ b/client/simple_address_range_bag_test.cc @@ -32,13 +32,13 @@ TEST(SimpleAddressRangeBag, Entry) { bag.Insert(reinterpret_cast(0x1000), 200); entry = TestBag::Iterator(bag).Next(); ASSERT_TRUE(entry); - EXPECT_EQ(entry->base, 0x1000); - EXPECT_EQ(entry->size, 200); + EXPECT_EQ(entry->base, 0x1000u); + EXPECT_EQ(entry->size, 200u); bag.Remove(reinterpret_cast(0x1000), 200); EXPECT_FALSE(entry->is_active()); - EXPECT_EQ(entry->base, 0); - EXPECT_EQ(entry->size, 0); + EXPECT_EQ(entry->base, 0u); + EXPECT_EQ(entry->size, 0u); } TEST(SimpleAddressRangeBag, SimpleAddressRangeBag) { @@ -59,13 +59,13 @@ TEST(SimpleAddressRangeBag, SimpleAddressRangeBag) { EXPECT_TRUE(bag.Remove(CheckedRange(0x3000, 30))); EXPECT_TRUE(bag.Remove(CheckedRange(0x3000, 30))); EXPECT_TRUE(bag.Remove(CheckedRange(0x3000, 30))); - EXPECT_EQ(bag.GetCount(), 2); + EXPECT_EQ(bag.GetCount(), 2u); EXPECT_FALSE(bag.Remove(CheckedRange(0x3000, 30))); - EXPECT_EQ(bag.GetCount(), 2); + EXPECT_EQ(bag.GetCount(), 2u); EXPECT_TRUE(bag.Remove(reinterpret_cast(0x1000), 10)); EXPECT_TRUE(bag.Remove(reinterpret_cast(0x2000), 20)); - EXPECT_EQ(bag.GetCount(), 0); + EXPECT_EQ(bag.GetCount(), 0u); } TEST(SimpleAddressRangeBag, CopyAndAssign) { From 70ae71fe512439a73ba1fa346fbe6f344a08dfc3 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 29 Feb 2016 14:36:08 -0800 Subject: [PATCH 14/16] Another Mac fix after 7217cc0a8f26 -- correct bad CrashpadInfo proctype layout Change-Id: Ieb8a45d8ff0526d970829f6a71915edd5a2c750f Reviewed-on: https://chromium-review.googlesource.com/329716 Reviewed-by: Robert Sesek --- snapshot/mac/process_types/crashpad_info.proctype | 3 +++ 1 file changed, 3 insertions(+) diff --git a/snapshot/mac/process_types/crashpad_info.proctype b/snapshot/mac/process_types/crashpad_info.proctype index af30d951..7d5383a3 100644 --- a/snapshot/mac/process_types/crashpad_info.proctype +++ b/snapshot/mac/process_types/crashpad_info.proctype @@ -37,6 +37,9 @@ PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) PROCESS_TYPE_STRUCT_MEMBER(uint8_t, padding_0) + // SimpleAddressRangeBag* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, extra_memory_ranges) + // SimpleStringDictionary* PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations) PROCESS_TYPE_STRUCT_END(CrashpadInfo) From c307f94f19cb7983ee585e9569875f1981fecde6 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 29 Feb 2016 13:28:05 -0800 Subject: [PATCH 15/16] Support custom streams in the minidump BUG=crashpad:95 Change-Id: Iee956906651dfd56e0ae3d2bcec82daabdc97067 Reviewed-on: https://chromium-review.googlesource.com/329733 Reviewed-by: Mark Mentovai --- client/crashpad_info.cc | 16 ++- client/crashpad_info.h | 47 +++++++- compat/non_win/dbghelp.h | 4 + handler/win/crashy_test_program.cc | 12 ++ minidump/minidump.gyp | 2 + minidump/minidump_file_writer.cc | 10 ++ minidump/minidump_test.gyp | 1 + minidump/minidump_user_stream_writer.cc | 73 ++++++++++++ minidump/minidump_user_stream_writer.h | 77 +++++++++++++ minidump/minidump_user_stream_writer_test.cc | 104 ++++++++++++++++++ snapshot/mac/module_snapshot_mac.cc | 5 + snapshot/mac/module_snapshot_mac.h | 1 + .../mac/process_types/crashpad_info.proctype | 3 + snapshot/minidump/module_snapshot_minidump.cc | 8 ++ snapshot/minidump/module_snapshot_minidump.h | 1 + snapshot/module_snapshot.h | 33 ++++++ snapshot/test/test_module_snapshot.cc | 5 + snapshot/test/test_module_snapshot.h | 6 + snapshot/win/end_to_end_test.py | 6 + snapshot/win/module_snapshot_win.cc | 47 ++++++++ snapshot/win/module_snapshot_win.h | 8 ++ snapshot/win/pe_image_reader.h | 1 + 22 files changed, 468 insertions(+), 2 deletions(-) create mode 100644 minidump/minidump_user_stream_writer.cc create mode 100644 minidump/minidump_user_stream_writer.h create mode 100644 minidump/minidump_user_stream_writer_test.cc diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 5913be81..36471a10 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -101,7 +101,8 @@ CrashpadInfo::CrashpadInfo() gather_indirectly_referenced_memory_(TriState::kUnset), padding_0_(0), extra_memory_ranges_(nullptr), - simple_annotations_(nullptr) + simple_annotations_(nullptr), + user_data_minidump_stream_head_(nullptr) #if !defined(NDEBUG) && defined(OS_WIN) , invalid_read_detection_(0xbadc0de) @@ -109,4 +110,17 @@ CrashpadInfo::CrashpadInfo() { } +void CrashpadInfo::AddUserDataMinidumpStream(uint32_t stream_type, + const void* data, + size_t size) { + auto to_be_added = new internal::UserDataMinidumpStreamListEntry(); + to_be_added->next = base::checked_cast( + reinterpret_cast(user_data_minidump_stream_head_)); + to_be_added->stream_type = stream_type; + to_be_added->base_address = + base::checked_cast(reinterpret_cast(data)); + to_be_added->size = base::checked_cast(size); + user_data_minidump_stream_head_ = to_be_added; +} + } // namespace crashpad diff --git a/client/crashpad_info.h b/client/crashpad_info.h index 4dd61abe..a181f997 100644 --- a/client/crashpad_info.h +++ b/client/crashpad_info.h @@ -29,6 +29,29 @@ namespace crashpad { +namespace internal { + +//! \brief A linked list of blocks representing custom streams in the minidump, +//! with addresses (and size) stored as uint64_t to simplify reading from +//! the handler process. +struct UserDataMinidumpStreamListEntry { + //! \brief The address of the next entry in the linked list. + uint64_t next; + + //! \brief The base address of the memory block in the target process' address + //! space that represents the user data stream. + uint64_t base_address; + + //! \brief The size of memory block in the target process' address space that + //! represents the user data stream. + uint64_t size; + + //! \brief The stream type identifier. + uint32_t stream_type; +}; + +} // namespace internal + //! \brief A structure that can be used by a Crashpad-enabled program to //! provide information to the Crashpad crash handler. //! @@ -89,7 +112,7 @@ struct CrashpadInfo { //! that it has been disabled. //! //! The Crashpad handler should not normally be disabled. More commonly, it - //! is appropraite to disable crash report upload by calling + //! is appropriate to disable crash report upload by calling //! Settings::SetUploadsEnabled(). void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) { crashpad_handler_behavior_ = crashpad_handler_behavior; @@ -130,6 +153,27 @@ struct CrashpadInfo { gather_indirectly_referenced_memory_ = gather_indirectly_referenced_memory; } + //! \brief Adds a custom stream to the minidump. + //! + //! The memory block referenced by \a data and \a size will added to the + //! minidump as separate stream with type \stream_type. The memory referred to + //! by \a data and \a size is owned by the caller and must remain valid while + //! it is in effect for the CrashpadInfo object. + //! + //! Note that streams will appear in the minidump in the reverse order to + //! which they are added. + //! + //! TODO(scottmg) This is currently only supported on Windows. + //! + //! \param[in] stream_type The stream type identifier to use. This should be + //! normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream` + //! which is `0xffff`. + //! \param[in] data The base pointer of the stream data. + //! \param[in] size The size of the stream data. + void AddUserDataMinidumpStream(uint32_t stream_type, + const void* data, + size_t size); + enum : uint32_t { kSignature = 'CPad', }; @@ -154,6 +198,7 @@ struct CrashpadInfo { uint8_t padding_0_; SimpleAddressRangeBag* extra_memory_ranges_; // weak SimpleStringDictionary* simple_annotations_; // weak + internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_; #if !defined(NDEBUG) && defined(OS_WIN) uint32_t invalid_read_detection_; diff --git a/compat/non_win/dbghelp.h b/compat/non_win/dbghelp.h index 2b7c974c..71ef1f4c 100644 --- a/compat/non_win/dbghelp.h +++ b/compat/non_win/dbghelp.h @@ -176,6 +176,10 @@ enum MINIDUMP_STREAM_TYPE { //! \brief The stream type for MINIDUMP_MEMORY_INFO_LIST. MemoryInfoListStream = 16, + + //! \brief Values greater than this value will not be used by the system + //! and can be used for custom user data streams. + LastReservedStream = 0xffff, }; //! \brief Information about the CPU (or CPUs) that ran the process that the diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index a3331398..a2b74134 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -225,6 +225,18 @@ int CrashyMain(int argc, wchar_t* argv[]) { crashpad::CrashpadInfo::GetCrashpadInfo() ->set_gather_indirectly_referenced_memory(TriState::kEnabled); + std::vector data_stream1(128, 'x'); + crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream( + 222222, + reinterpret_cast(data_stream1.data()), + data_stream1.size()); + + std::vector data_stream2(4096, 'z'); + crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream( + 333333, + reinterpret_cast(data_stream2.data()), + data_stream2.size()); + AllocateMemoryOfVariousProtections(); if (InitializeCriticalSectionWithDebugInfoIfPossible( diff --git a/minidump/minidump.gyp b/minidump/minidump.gyp index 3f714668..35528060 100644 --- a/minidump/minidump.gyp +++ b/minidump/minidump.gyp @@ -72,6 +72,8 @@ 'minidump_thread_writer.h', 'minidump_unloaded_module_writer.cc', 'minidump_unloaded_module_writer.h', + 'minidump_user_stream_writer.cc', + 'minidump_user_stream_writer.h', 'minidump_writable.cc', 'minidump_writable.h', 'minidump_writer_util.cc', diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index 0c79c78d..7f8f47d5 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -28,8 +28,10 @@ #include "minidump/minidump_thread_id_map.h" #include "minidump/minidump_thread_writer.h" #include "minidump/minidump_unloaded_module_writer.h" +#include "minidump/minidump_user_stream_writer.h" #include "minidump/minidump_writer_util.h" #include "snapshot/exception_snapshot.h" +#include "snapshot/module_snapshot.h" #include "snapshot/process_snapshot.h" #include "util/file/file_writer.h" #include "util/numeric/safe_assignment.h" @@ -96,6 +98,14 @@ void MinidumpFileWriter::InitializeFromSnapshot( module_list->InitializeFromSnapshot(process_snapshot->Modules()); AddStream(std::move(module_list)); + for (const auto& module : process_snapshot->Modules()) { + for (const UserMinidumpStream* stream : module->CustomMinidumpStreams()) { + auto user_stream = make_scoped_ptr(new MinidumpUserStreamWriter()); + user_stream->InitializeFromSnapshot(stream); + AddStream(std::move(user_stream)); + } + } + auto unloaded_modules = process_snapshot->UnloadedModules(); if (!unloaded_modules.empty()) { auto unloaded_module_list = diff --git a/minidump/minidump_test.gyp b/minidump/minidump_test.gyp index 30a179f1..cdae4caa 100644 --- a/minidump/minidump_test.gyp +++ b/minidump/minidump_test.gyp @@ -50,6 +50,7 @@ 'minidump_thread_id_map_test.cc', 'minidump_thread_writer_test.cc', 'minidump_unloaded_module_writer_test.cc', + 'minidump_user_stream_writer_test.cc', 'minidump_writable_test.cc', 'test/minidump_context_test_util.cc', 'test/minidump_context_test_util.h', diff --git a/minidump/minidump_user_stream_writer.cc b/minidump/minidump_user_stream_writer.cc new file mode 100644 index 00000000..7332cd01 --- /dev/null +++ b/minidump/minidump_user_stream_writer.cc @@ -0,0 +1,73 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "minidump/minidump_user_stream_writer.h" + +#include "util/file/file_writer.h" + +namespace crashpad { + +MinidumpUserStreamWriter::MinidumpUserStreamWriter() + : stream_type_(0), reader_() { +} + +MinidumpUserStreamWriter::~MinidumpUserStreamWriter() { +} + +void MinidumpUserStreamWriter::InitializeFromSnapshot( + const UserMinidumpStream* stream) { + DCHECK_EQ(state(), kStateMutable); + + stream_type_ = stream->stream_type(); + if (stream->memory()) + stream->memory()->Read(&reader_); +} + +bool MinidumpUserStreamWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + DCHECK_NE(stream_type_, 0u); + return MinidumpStreamWriter::Freeze(); +} + +size_t MinidumpUserStreamWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + return reader_.size(); +} + +std::vector +MinidumpUserStreamWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + return std::vector(); +} + +bool MinidumpUserStreamWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(reader_.data(), reader_.size()); +} + +MinidumpStreamType MinidumpUserStreamWriter::StreamType() const { + return static_cast(stream_type_); +} + +MinidumpUserStreamWriter::MemoryReader::~MemoryReader() {} + +bool MinidumpUserStreamWriter::MemoryReader::MemorySnapshotDelegateRead( + void* data, + size_t size) { + data_.resize(size); + memcpy(&data_[0], data, size); + return true; +} + +} // namespace crashpad diff --git a/minidump/minidump_user_stream_writer.h b/minidump/minidump_user_stream_writer.h new file mode 100644 index 00000000..b31247a3 --- /dev/null +++ b/minidump/minidump_user_stream_writer.h @@ -0,0 +1,77 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_ + +#include +#include +#include + +#include + +#include "base/macros.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" +#include "snapshot/module_snapshot.h" + +namespace crashpad { + +//! \brief The writer for a MINIDUMP_USER_STREAM in a minidump file. +class MinidumpUserStreamWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpUserStreamWriter(); + ~MinidumpUserStreamWriter() override; + + //! \brief Initializes a MINIDUMP_USER_STREAM based on \a stream. + //! + //! \param[in] stream The memory and stream type to use as source data. + //! + //! \note Valid in #kStateMutable. + void InitializeFromSnapshot(const UserMinidumpStream* stream); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + class MemoryReader : public MemorySnapshot::Delegate { + public: + ~MemoryReader() override; + bool MemorySnapshotDelegateRead(void* data, size_t size) override; + + const void* data() const { + return reinterpret_cast(data_.data()); + } + size_t size() const { return data_.size(); } + + private: + std::vector data_; + }; + + uint32_t stream_type_; + MemoryReader reader_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpUserStreamWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_ diff --git a/minidump/minidump_user_stream_writer_test.cc b/minidump/minidump_user_stream_writer_test.cc new file mode 100644 index 00000000..ae58d825 --- /dev/null +++ b/minidump/minidump_user_stream_writer_test.cc @@ -0,0 +1,104 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "minidump/minidump_user_stream_writer.h" + +#include +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_memory_snapshot.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +// The user stream is expected to be the only stream. +void GetUserStream(const std::string& file_contents, + MINIDUMP_LOCATION_DESCRIPTOR* user_stream_location, + uint32_t stream_type) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kUserStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + const size_t kDirectoryIndex = 0; + + ASSERT_EQ(stream_type, directory[kDirectoryIndex].StreamType); + EXPECT_EQ(kUserStreamOffset, directory[kDirectoryIndex].Location.Rva); + *user_stream_location = directory[kDirectoryIndex].Location; +} + +TEST(MinidumpUserStreamWriter, NoData) { + MinidumpFileWriter minidump_file_writer; + auto user_stream_writer = make_scoped_ptr(new MinidumpUserStreamWriter()); + const uint32_t kTestStreamId = 0x123456; + auto stream = make_scoped_ptr(new UserMinidumpStream(kTestStreamId, nullptr)); + user_stream_writer->InitializeFromSnapshot(stream.get()); + minidump_file_writer.AddStream(std::move(user_stream_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY), + string_file.string().size()); + + MINIDUMP_LOCATION_DESCRIPTOR user_stream_location; + ASSERT_NO_FATAL_FAILURE(GetUserStream( + string_file.string(), &user_stream_location, kTestStreamId)); + EXPECT_EQ(0u, user_stream_location.DataSize); +} + +TEST(MinidumpUserStreamWriter, OneStream) { + MinidumpFileWriter minidump_file_writer; + auto user_stream_writer = make_scoped_ptr(new MinidumpUserStreamWriter()); + const uint32_t kTestStreamId = 0x123456; + + TestMemorySnapshot* test_data = new TestMemorySnapshot(); + test_data->SetAddress(97865); + const size_t kStreamSize = 128; + test_data->SetSize(kStreamSize); + test_data->SetValue('c'); + auto stream = + make_scoped_ptr(new UserMinidumpStream(kTestStreamId, test_data)); + user_stream_writer->InitializeFromSnapshot(stream.get()); + minidump_file_writer.AddStream(std::move(user_stream_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + kStreamSize, + string_file.string().size()); + + MINIDUMP_LOCATION_DESCRIPTOR user_stream_location; + ASSERT_NO_FATAL_FAILURE(GetUserStream( + string_file.string(), &user_stream_location, kTestStreamId)); + EXPECT_EQ(kStreamSize, user_stream_location.DataSize); + const std::string stream_data = string_file.string().substr( + user_stream_location.Rva, user_stream_location.DataSize); + EXPECT_EQ(std::string(kStreamSize, 'c'), stream_data); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/mac/module_snapshot_mac.cc b/snapshot/mac/module_snapshot_mac.cc index 4f4df2a5..4eaa12b1 100644 --- a/snapshot/mac/module_snapshot_mac.cc +++ b/snapshot/mac/module_snapshot_mac.cc @@ -187,5 +187,10 @@ std::set> ModuleSnapshotMac::ExtraMemoryRanges() const { return std::set>(); } +std::vector +ModuleSnapshotMac::CustomMinidumpStreams() const { + return std::vector(); +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/mac/module_snapshot_mac.h b/snapshot/mac/module_snapshot_mac.h index 0c6bd69a..23904a5e 100644 --- a/snapshot/mac/module_snapshot_mac.h +++ b/snapshot/mac/module_snapshot_mac.h @@ -80,6 +80,7 @@ class ModuleSnapshotMac final : public ModuleSnapshot { std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; private: std::string name_; diff --git a/snapshot/mac/process_types/crashpad_info.proctype b/snapshot/mac/process_types/crashpad_info.proctype index 7d5383a3..90ed0d44 100644 --- a/snapshot/mac/process_types/crashpad_info.proctype +++ b/snapshot/mac/process_types/crashpad_info.proctype @@ -42,4 +42,7 @@ PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) // SimpleStringDictionary* PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations) + + // UserDataStreamListEntry* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, user_data_minidump_stream_head) PROCESS_TYPE_STRUCT_END(CrashpadInfo) diff --git a/snapshot/minidump/module_snapshot_minidump.cc b/snapshot/minidump/module_snapshot_minidump.cc index c83de596..f5132270 100644 --- a/snapshot/minidump/module_snapshot_minidump.cc +++ b/snapshot/minidump/module_snapshot_minidump.cc @@ -138,9 +138,17 @@ ModuleSnapshotMinidump::AnnotationsSimpleMap() const { std::set> ModuleSnapshotMinidump::ExtraMemoryRanges() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 return std::set>(); } +std::vector +ModuleSnapshotMinidump::CustomMinidumpStreams() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::vector(); +} + bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo( FileReaderInterface* file_reader, const MINIDUMP_LOCATION_DESCRIPTOR* diff --git a/snapshot/minidump/module_snapshot_minidump.h b/snapshot/minidump/module_snapshot_minidump.h index dc375f03..3d63f2ac 100644 --- a/snapshot/minidump/module_snapshot_minidump.h +++ b/snapshot/minidump/module_snapshot_minidump.h @@ -77,6 +77,7 @@ class ModuleSnapshotMinidump final : public ModuleSnapshot { std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; private: // Initializes data carried in a MinidumpModuleCrashpadInfo structure on diff --git a/snapshot/module_snapshot.h b/snapshot/module_snapshot.h index 3a1c3216..3e5d18af 100644 --- a/snapshot/module_snapshot.h +++ b/snapshot/module_snapshot.h @@ -23,11 +23,35 @@ #include #include +#include "base/memory/scoped_ptr.h" #include "util/misc/uuid.h" #include "util/numeric/checked_range.h" +#include "snapshot/memory_snapshot.h" namespace crashpad { +class MemorySnapshot; + +//! \brief Information describing a custom user data stream in a minidump. +class UserMinidumpStream { + public: + //! \brief Constructs a UserMinidumpStream, takes ownership of \a memory. + UserMinidumpStream(uint32_t stream_type, MemorySnapshot* memory) + : memory_(memory), stream_type_(stream_type) {} + + const MemorySnapshot* memory() const { return memory_.get(); } + uint32_t stream_type() const { return stream_type_; } + + private: + //! \brief The memory representing the custom minidump stream. + scoped_ptr memory_; + + //! \brief The stream type that the minidump stream will be tagged with. + uint32_t stream_type_; + + DISALLOW_COPY_AND_ASSIGN(UserMinidumpStream); +}; + //! \brief An abstract interface to a snapshot representing a code module //! (binary image) loaded into a snapshot process. class ModuleSnapshot { @@ -174,6 +198,15 @@ class ModuleSnapshot { //! \brief Returns a set of extra memory ranges specified in the module as //! being desirable to include in the crash dump. virtual std::set> ExtraMemoryRanges() const = 0; + + //! \brief Returns a list of custom minidump stream specified in the module to + //! be included in the crash dump. + //! + //! \return The caller does not take ownership of the returned objects, they + //! are scoped to the lifetime of the ModuleSnapshot object that they were + //! obtained from. + virtual std::vector CustomMinidumpStreams() + const = 0; }; } // namespace crashpad diff --git a/snapshot/test/test_module_snapshot.cc b/snapshot/test/test_module_snapshot.cc index c853a780..08776d65 100644 --- a/snapshot/test/test_module_snapshot.cc +++ b/snapshot/test/test_module_snapshot.cc @@ -98,5 +98,10 @@ std::set> TestModuleSnapshot::ExtraMemoryRanges() const { return extra_memory_ranges_; } +std::vector +TestModuleSnapshot::CustomMinidumpStreams() const { + return custom_minidump_streams_; +} + } // namespace test } // namespace crashpad diff --git a/snapshot/test/test_module_snapshot.h b/snapshot/test/test_module_snapshot.h index bbe6193d..7589d628 100644 --- a/snapshot/test/test_module_snapshot.h +++ b/snapshot/test/test_module_snapshot.h @@ -24,6 +24,7 @@ #include "base/macros.h" #include "snapshot/module_snapshot.h" +#include "util/stdlib/pointer_container.h" namespace crashpad { namespace test { @@ -79,6 +80,9 @@ class TestModuleSnapshot final : public ModuleSnapshot { const std::set>& extra_memory_ranges) { extra_memory_ranges_ = extra_memory_ranges; } + void AddCustomMinidumpStream(const UserMinidumpStream* stream) { + custom_minidump_streams_.push_back(stream); + } // ModuleSnapshot: @@ -100,6 +104,7 @@ class TestModuleSnapshot final : public ModuleSnapshot { std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; private: std::string name_; @@ -115,6 +120,7 @@ class TestModuleSnapshot final : public ModuleSnapshot { std::vector annotations_vector_; std::map annotations_simple_map_; std::set> extra_memory_ranges_; + PointerVector custom_minidump_streams_; DISALLOW_COPY_AND_ASSIGN(TestModuleSnapshot); }; diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index 404d6d3a..a3506b64 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -290,6 +290,12 @@ def RunTests(cdb_path, r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?', 'extra memory removal') + out = CdbRun(cdb_path, dump_path, '.dumpdebug') + out.Check(r'type \?\?\? \(333333\), size 00001000', + 'first user stream') + out.Check(r'type \?\?\? \(222222\), size 00000080', + 'second user stream') + if z7_dump_path: out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm') out.Check('This dump file has an exception of interest stored in it', diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index b86e2029..54856cc8 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -15,7 +15,9 @@ #include "snapshot/win/module_snapshot_win.h" #include "base/strings/utf_string_conversions.h" +#include "client/crashpad_info.h" #include "client/simple_address_range_bag.h" +#include "snapshot/win/memory_snapshot_win.h" #include "snapshot/win/pe_image_annotations_reader.h" #include "snapshot/win/pe_image_reader.h" #include "util/misc/tri_state.h" @@ -196,6 +198,24 @@ std::set> ModuleSnapshotWin::ExtraMemoryRanges() const { return ranges; } +std::vector +ModuleSnapshotWin::CustomMinidumpStreams() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + streams_.clear(); + if (process_reader_->Is64Bit()) { + GetCrashpadUserMinidumpStreams( + &streams_); + } else { + GetCrashpadUserMinidumpStreams( + &streams_); + } + + std::vector result; + for (const auto* stream : streams_) + result.push_back(stream); + return result; +} + template void ModuleSnapshotWin::GetCrashpadOptionsInternal( CrashpadInfoClientOptions* options) { @@ -262,5 +282,32 @@ void ModuleSnapshotWin::GetCrashpadExtraMemoryRanges( } } +template +void ModuleSnapshotWin::GetCrashpadUserMinidumpStreams( + PointerVector* streams) const { + process_types::CrashpadInfo crashpad_info; + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) + return; + + for (uint64_t cur = crashpad_info.user_data_minidump_stream_head; cur;) { + internal::UserDataMinidumpStreamListEntry list_entry; + if (!process_reader_->ReadMemory( + cur, sizeof(list_entry), &list_entry)) { + LOG(WARNING) << "could not read user data stream entry from " + << base::UTF16ToUTF8(name_); + return; + } + + scoped_ptr memory( + new internal::MemorySnapshotWin()); + memory->Initialize( + process_reader_, list_entry.base_address, list_entry.size); + streams->push_back( + new UserMinidumpStream(list_entry.stream_type, memory.release())); + + cur = list_entry.next; + } +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/win/module_snapshot_win.h b/snapshot/win/module_snapshot_win.h index 85aeb123..dd12ae8b 100644 --- a/snapshot/win/module_snapshot_win.h +++ b/snapshot/win/module_snapshot_win.h @@ -29,6 +29,7 @@ #include "snapshot/win/process_reader_win.h" #include "util/misc/initialization_state.h" #include "util/misc/initialization_state_dcheck.h" +#include "util/stdlib/pointer_container.h" #include "util/win/process_info.h" namespace crashpad { @@ -86,6 +87,7 @@ class ModuleSnapshotWin final : public ModuleSnapshot { std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; private: template @@ -95,6 +97,10 @@ class ModuleSnapshotWin final : public ModuleSnapshot { void GetCrashpadExtraMemoryRanges( std::set>* ranges) const; + template + void GetCrashpadUserMinidumpStreams( + PointerVector* streams) const; + // Initializes vs_fixed_file_info_ if it has not yet been initialized, and // returns a pointer to it. Returns nullptr on failure, with a message logged // on the first call. @@ -107,6 +113,8 @@ class ModuleSnapshotWin final : public ModuleSnapshot { ProcessReaderWin* process_reader_; // weak time_t timestamp_; uint32_t age_; + // Too const-y: https://crashpad.chromium.org/bug/9. + mutable PointerVector streams_; InitializationStateDcheck initialized_; // VSFixedFileInfo() is logically const, but updates these members on the diff --git a/snapshot/win/pe_image_reader.h b/snapshot/win/pe_image_reader.h index 96529bf8..6e5ef7d7 100644 --- a/snapshot/win/pe_image_reader.h +++ b/snapshot/win/pe_image_reader.h @@ -45,6 +45,7 @@ struct CrashpadInfo { uint8_t padding_0; typename Traits::Pointer extra_address_ranges; typename Traits::Pointer simple_annotations; + typename Traits::Pointer user_data_minidump_stream_head; }; } // namespace process_types From f64ef73d8bbaab77b49255102aead1f5c0586e0e Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 15 Mar 2016 11:27:32 -0700 Subject: [PATCH 16/16] Update project status docs for Windows Change-Id: Iefd22eace38e1af3003020de8a08d851da8cec9a Reviewed-on: https://chromium-review.googlesource.com/332663 Reviewed-by: Mark Mentovai --- doc/status.ad | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/status.ad b/doc/status.ad index e980b9d0..5edbadcf 100644 --- a/doc/status.ad +++ b/doc/status.ad @@ -19,18 +19,16 @@ == Completed Crashpad currently consists of a crash-reporting client and some related tools -for Mac OS X. The core client work for Mac OS X is substantially complete. -Crashpad has become the crash reporter client for +for Mac OS X and Windows. The core client work for both platforms is +substantially complete. Crashpad became the crash reporter client for https://dev.chromium.org/Home[Chromium] on Mac OS X as of https://chromium.googlesource.com/chromium/src/+/d413b2dcb54d523811d386f1ff4084f677a6d089[March +2015], and for https://dev.chromium.org/Home[Chromium] on Windows as of +https://chromium.googlesource.com/chromium/src/+/cfa5b01bb1d06bf96967bd37e21a44752801948c[November 2015]. == In Progress -Crashpad is actively being bootstrapped on -https://crashpad.chromium.org/bug/1[Windows]. All major components are now -working on Windows, and integration into Chromium is expected shortly. - Initial work on a Crashpad client for https://crashpad.chromium.org/bug/30[Android] has begun. This is currently in the design phase.