From 4794225f22583a08732cb9ad4356106013a220f3 Mon Sep 17 00:00:00 2001 From: Patrick Monette Date: Mon, 18 Jan 2016 15:05:20 -0500 Subject: [PATCH] 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_