mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-15 01:50:34 +08:00
win: Make reading NT_IMAGE_HEADERS work cross-bitness
Factor out some test launching code used in cross-bitness tests. R=mark@chromium.org BUG=crashpad:50 Review URL: https://codereview.chromium.org/1352323002 .
This commit is contained in:
parent
bf556829d9
commit
4a34a3dd89
@ -79,6 +79,7 @@
|
|||||||
'win/pe_image_annotations_reader_test.cc',
|
'win/pe_image_annotations_reader_test.cc',
|
||||||
'win/pe_image_reader_test.cc',
|
'win/pe_image_reader_test.cc',
|
||||||
'win/process_reader_win_test.cc',
|
'win/process_reader_win_test.cc',
|
||||||
|
'win/process_snapshot_win_test.cc',
|
||||||
'win/system_snapshot_win_test.cc',
|
'win/system_snapshot_win_test.cc',
|
||||||
],
|
],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
@ -96,6 +97,8 @@
|
|||||||
['OS=="win"', {
|
['OS=="win"', {
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'crashpad_snapshot_test_crashing_child',
|
'crashpad_snapshot_test_crashing_child',
|
||||||
|
'crashpad_snapshot_test_image_reader',
|
||||||
|
'crashpad_snapshot_test_image_reader_module',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
@ -149,6 +152,30 @@
|
|||||||
'win/crashpad_snapshot_test_crashing_child.cc',
|
'win/crashpad_snapshot_test_crashing_child.cc',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'target_name': 'crashpad_snapshot_test_image_reader',
|
||||||
|
'type': 'executable',
|
||||||
|
'dependencies': [
|
||||||
|
'../client/client.gyp:crashpad_client',
|
||||||
|
'../compat/compat.gyp:crashpad_compat',
|
||||||
|
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||||
|
'../util/util.gyp:crashpad_util',
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'win/crashpad_snapshot_test_image_reader.cc',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'target_name': 'crashpad_snapshot_test_image_reader_module',
|
||||||
|
'type': 'loadable_module',
|
||||||
|
'dependencies': [
|
||||||
|
'../client/client.gyp:crashpad_client',
|
||||||
|
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'win/crashpad_snapshot_test_image_reader_module.cc',
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
|
@ -12,8 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
@ -33,7 +31,7 @@ int main(int argc, char* argv[]) {
|
|||||||
CHECK(client.UseHandler());
|
CHECK(client.UseHandler());
|
||||||
|
|
||||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
CHECK_NE(out, INVALID_HANDLE_VALUE);
|
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
|
||||||
crashpad::WinVMAddress break_address = CurrentAddress();
|
crashpad::WinVMAddress break_address = CurrentAddress();
|
||||||
crashpad::CheckedWriteFile(out, &break_address, sizeof(break_address));
|
crashpad::CheckedWriteFile(out, &break_address, sizeof(break_address));
|
||||||
|
|
||||||
|
38
snapshot/win/crashpad_snapshot_test_image_reader.cc
Normal file
38
snapshot/win/crashpad_snapshot_test_image_reader.cc
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "util/file/file_io.h"
|
||||||
|
#include "util/win/scoped_handle.h"
|
||||||
|
|
||||||
|
int wmain(int argc, wchar_t* argv[]) {
|
||||||
|
CHECK_EQ(argc, 2);
|
||||||
|
|
||||||
|
crashpad::ScopedKernelHANDLE done(CreateEvent(nullptr, true, false, argv[1]));
|
||||||
|
|
||||||
|
PCHECK(LoadLibrary(L"crashpad_snapshot_test_image_reader_module.dll"))
|
||||||
|
<< "LoadLibrary";
|
||||||
|
|
||||||
|
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
|
||||||
|
char c = ' ';
|
||||||
|
crashpad::CheckedWriteFile(out, &c, sizeof(c));
|
||||||
|
|
||||||
|
CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(done.get(), INFINITE));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
19
snapshot/win/crashpad_snapshot_test_image_reader_module.cc
Normal file
19
snapshot/win/crashpad_snapshot_test_image_reader_module.cc
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
@ -24,7 +24,8 @@
|
|||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "snapshot/win/process_snapshot_win.h"
|
#include "snapshot/win/process_snapshot_win.h"
|
||||||
#include "test/paths.h"
|
#include "test/paths.h"
|
||||||
#include "test/win/win_child_process.h"
|
#include "test/win/child_launcher.h"
|
||||||
|
#include "util/file/file_io.h"
|
||||||
#include "util/thread/thread.h"
|
#include "util/thread/thread.h"
|
||||||
#include "util/win/exception_handler_server.h"
|
#include "util/win/exception_handler_server.h"
|
||||||
#include "util/win/registration_protocol_win.h"
|
#include "util/win/registration_protocol_win.h"
|
||||||
@ -157,45 +158,14 @@ void TestCrashingChild(const base::string16& directory_modification) {
|
|||||||
.Append(test_executable.BaseName().RemoveFinalExtension().value() +
|
.Append(test_executable.BaseName().RemoveFinalExtension().value() +
|
||||||
L"_crashing_child.exe")
|
L"_crashing_child.exe")
|
||||||
.value();
|
.value();
|
||||||
|
ChildLauncher child(child_test_executable, base::UTF8ToUTF16(pipe_name));
|
||||||
// Create a pipe for the stdout of the child.
|
child.Start();
|
||||||
SECURITY_ATTRIBUTES security_attributes = {0};
|
|
||||||
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
||||||
security_attributes.bInheritHandle = true;
|
|
||||||
HANDLE stdout_read;
|
|
||||||
HANDLE stdout_write;
|
|
||||||
ASSERT_TRUE(CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0));
|
|
||||||
ScopedFileHANDLE read_handle(stdout_read);
|
|
||||||
ScopedFileHANDLE write_handle(stdout_write);
|
|
||||||
ASSERT_TRUE(SetHandleInformation(read_handle.get(), HANDLE_FLAG_INHERIT, 0));
|
|
||||||
|
|
||||||
std::wstring command_line =
|
|
||||||
child_test_executable + L" " + base::UTF8ToUTF16(pipe_name);
|
|
||||||
STARTUPINFO startup_info = {0};
|
|
||||||
startup_info.cb = sizeof(startup_info);
|
|
||||||
startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
startup_info.hStdOutput = write_handle.get();
|
|
||||||
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
|
||||||
startup_info.dwFlags = STARTF_USESTDHANDLES;
|
|
||||||
PROCESS_INFORMATION process_information;
|
|
||||||
ASSERT_TRUE(CreateProcess(child_test_executable.c_str(),
|
|
||||||
&command_line[0],
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
true,
|
|
||||||
0,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
&startup_info,
|
|
||||||
&process_information));
|
|
||||||
// Take ownership of the two process handles returned.
|
|
||||||
ScopedKernelHANDLE process_main_thread_handle(process_information.hThread);
|
|
||||||
ScopedKernelHANDLE process_handle(process_information.hProcess);
|
|
||||||
|
|
||||||
// The child tells us (approximately) where it will crash.
|
// The child tells us (approximately) where it will crash.
|
||||||
WinVMAddress break_near_address;
|
WinVMAddress break_near_address;
|
||||||
LoggingReadFile(
|
LoggingReadFile(child.stdout_read_handle(),
|
||||||
read_handle.get(), &break_near_address, sizeof(break_near_address));
|
&break_near_address,
|
||||||
|
sizeof(break_near_address));
|
||||||
delegate.set_break_near(break_near_address);
|
delegate.set_break_near(break_near_address);
|
||||||
|
|
||||||
// Wait for the child to crash and the exception information to be validated.
|
// Wait for the child to crash and the exception information to be validated.
|
||||||
|
@ -61,6 +61,10 @@ class ModuleSnapshotWin final : public ModuleSnapshot {
|
|||||||
//! \param[out] options Options set in the module's CrashpadInfo structure.
|
//! \param[out] options Options set in the module's CrashpadInfo structure.
|
||||||
void GetCrashpadOptions(CrashpadInfoClientOptions* options);
|
void GetCrashpadOptions(CrashpadInfoClientOptions* options);
|
||||||
|
|
||||||
|
//! \brief Returns the PEImageReader used to read this module. Only valid
|
||||||
|
//! after Initialize() is called.
|
||||||
|
const PEImageReader& pe_image_reader() const { return *pe_image_reader_; }
|
||||||
|
|
||||||
// ModuleSnapshot:
|
// ModuleSnapshot:
|
||||||
|
|
||||||
std::string Name() const override;
|
std::string Name() const override;
|
||||||
|
@ -70,8 +70,13 @@ bool PEImageReader::GetCrashpadInfo(
|
|||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
IMAGE_SECTION_HEADER section;
|
IMAGE_SECTION_HEADER section;
|
||||||
if (!GetSectionByName("CPADinfo", §ion))
|
if (process_reader_->Is64Bit()) {
|
||||||
return false;
|
if (!GetSectionByName<IMAGE_NT_HEADERS64>("CPADinfo", §ion))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!GetSectionByName<IMAGE_NT_HEADERS32>("CPADinfo", §ion))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo)) {
|
if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo)) {
|
||||||
LOG(WARNING) << "small crashpad info section size "
|
LOG(WARNING) << "small crashpad info section size "
|
||||||
@ -114,9 +119,22 @@ bool PEImageReader::GetCrashpadInfo(
|
|||||||
|
|
||||||
bool PEImageReader::DebugDirectoryInformation(UUID* uuid,
|
bool PEImageReader::DebugDirectoryInformation(UUID* uuid,
|
||||||
DWORD* age,
|
DWORD* age,
|
||||||
std::string* pdbname) {
|
std::string* pdbname) const {
|
||||||
|
if (process_reader_->Is64Bit()) {
|
||||||
|
return ReadDebugDirectoryInformation<IMAGE_NT_HEADERS64>(
|
||||||
|
uuid, age, pdbname);
|
||||||
|
} else {
|
||||||
|
return ReadDebugDirectoryInformation<IMAGE_NT_HEADERS32>(
|
||||||
|
uuid, age, pdbname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class NtHeadersType>
|
||||||
|
bool PEImageReader::ReadDebugDirectoryInformation(UUID* uuid,
|
||||||
|
DWORD* age,
|
||||||
|
std::string* pdbname) const {
|
||||||
WinVMAddress nt_headers_address;
|
WinVMAddress nt_headers_address;
|
||||||
IMAGE_NT_HEADERS nt_headers;
|
NtHeadersType nt_headers;
|
||||||
if (!ReadNtHeaders(&nt_headers_address, &nt_headers))
|
if (!ReadNtHeaders(&nt_headers_address, &nt_headers))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -173,9 +191,9 @@ bool PEImageReader::DebugDirectoryInformation(UUID* uuid,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(scottmg): This needs to be made cross-bitness supporting.
|
template <class NtHeadersType>
|
||||||
bool PEImageReader::ReadNtHeaders(WinVMAddress* nt_headers_address,
|
bool PEImageReader::ReadNtHeaders(WinVMAddress* nt_headers_address,
|
||||||
IMAGE_NT_HEADERS* nt_headers) const {
|
NtHeadersType* nt_headers) const {
|
||||||
IMAGE_DOS_HEADER dos_header;
|
IMAGE_DOS_HEADER dos_header;
|
||||||
if (!CheckedReadMemory(Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) {
|
if (!CheckedReadMemory(Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) {
|
||||||
LOG(WARNING) << "could not read dos header of " << module_name_;
|
LOG(WARNING) << "could not read dos header of " << module_name_;
|
||||||
@ -189,7 +207,7 @@ bool PEImageReader::ReadNtHeaders(WinVMAddress* nt_headers_address,
|
|||||||
|
|
||||||
*nt_headers_address = Address() + dos_header.e_lfanew;
|
*nt_headers_address = Address() + dos_header.e_lfanew;
|
||||||
if (!CheckedReadMemory(
|
if (!CheckedReadMemory(
|
||||||
*nt_headers_address, sizeof(IMAGE_NT_HEADERS), nt_headers)) {
|
*nt_headers_address, sizeof(NtHeadersType), nt_headers)) {
|
||||||
LOG(WARNING) << "could not read nt headers of " << module_name_;
|
LOG(WARNING) << "could not read nt headers of " << module_name_;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -202,6 +220,7 @@ bool PEImageReader::ReadNtHeaders(WinVMAddress* nt_headers_address,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class NtHeadersType>
|
||||||
bool PEImageReader::GetSectionByName(const std::string& name,
|
bool PEImageReader::GetSectionByName(const std::string& name,
|
||||||
IMAGE_SECTION_HEADER* section) const {
|
IMAGE_SECTION_HEADER* section) const {
|
||||||
if (name.size() > sizeof(section->Name)) {
|
if (name.size() > sizeof(section->Name)) {
|
||||||
@ -210,12 +229,12 @@ bool PEImageReader::GetSectionByName(const std::string& name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
WinVMAddress nt_headers_address;
|
WinVMAddress nt_headers_address;
|
||||||
IMAGE_NT_HEADERS nt_headers;
|
NtHeadersType nt_headers;
|
||||||
if (!ReadNtHeaders(&nt_headers_address, &nt_headers))
|
if (!ReadNtHeaders(&nt_headers_address, &nt_headers))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WinVMAddress first_section_address =
|
WinVMAddress first_section_address =
|
||||||
nt_headers_address + offsetof(IMAGE_NT_HEADERS, OptionalHeader) +
|
nt_headers_address + offsetof(NtHeadersType, OptionalHeader) +
|
||||||
nt_headers.FileHeader.SizeOfOptionalHeader;
|
nt_headers.FileHeader.SizeOfOptionalHeader;
|
||||||
for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) {
|
for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) {
|
||||||
WinVMAddress section_address =
|
WinVMAddress section_address =
|
||||||
|
@ -102,14 +102,25 @@ class PEImageReader {
|
|||||||
//! \param[out] pdbname Name of the pdb file.
|
//! \param[out] pdbname Name of the pdb file.
|
||||||
//! \return `true` on success, or `false` if the module has no debug directory
|
//! \return `true` on success, or `false` if the module has no debug directory
|
||||||
//! entry.
|
//! entry.
|
||||||
bool DebugDirectoryInformation(UUID* uuid, DWORD* age, std::string* pdbname);
|
bool DebugDirectoryInformation(UUID* uuid,
|
||||||
|
DWORD* age,
|
||||||
|
std::string* pdbname) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
//! \brief Implementation helper for DebugDirectoryInformation() templated by
|
||||||
|
//! `IMAGE_NT_HEADERS` type for different bitnesses.
|
||||||
|
template <class NtHeadersType>
|
||||||
|
bool ReadDebugDirectoryInformation(UUID* uuid,
|
||||||
|
DWORD* age,
|
||||||
|
std::string* pdbname) const;
|
||||||
|
|
||||||
//! \brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image.
|
//! \brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image.
|
||||||
|
template <class NtHeadersType>
|
||||||
bool ReadNtHeaders(WinVMAddress* nt_header_address,
|
bool ReadNtHeaders(WinVMAddress* nt_header_address,
|
||||||
IMAGE_NT_HEADERS* nt_headers) const;
|
NtHeadersType* nt_headers) const;
|
||||||
|
|
||||||
//! \brief Finds a given section by name in the image.
|
//! \brief Finds a given section by name in the image.
|
||||||
|
template <class NtHeadersType>
|
||||||
bool GetSectionByName(const std::string& name,
|
bool GetSectionByName(const std::string& name,
|
||||||
IMAGE_SECTION_HEADER* section) const;
|
IMAGE_SECTION_HEADER* section) const;
|
||||||
|
|
||||||
|
107
snapshot/win/process_snapshot_win_test.cc
Normal file
107
snapshot/win/process_snapshot_win_test.cc
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "snapshot/win/process_snapshot_win.h"
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "snapshot/win/module_snapshot_win.h"
|
||||||
|
#include "snapshot/win/pe_image_reader.h"
|
||||||
|
#include "snapshot/win/process_reader_win.h"
|
||||||
|
#include "util/file/file_io.h"
|
||||||
|
#include "util/win/scoped_handle.h"
|
||||||
|
#include "util/win/scoped_process_suspend.h"
|
||||||
|
#include "test/paths.h"
|
||||||
|
#include "test/win/child_launcher.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void TestImageReaderChild(const base::string16& directory_modification) {
|
||||||
|
UUID done_uuid(UUID::InitializeWithNewTag{});
|
||||||
|
ScopedKernelHANDLE done(
|
||||||
|
CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str()));
|
||||||
|
ASSERT_TRUE(done.get());
|
||||||
|
|
||||||
|
base::FilePath test_executable = Paths::Executable();
|
||||||
|
std::wstring child_test_executable =
|
||||||
|
test_executable.DirName()
|
||||||
|
.Append(directory_modification)
|
||||||
|
.Append(test_executable.BaseName().RemoveFinalExtension().value() +
|
||||||
|
L"_image_reader.exe")
|
||||||
|
.value();
|
||||||
|
ChildLauncher child(child_test_executable, done_uuid.ToString16());
|
||||||
|
child.Start();
|
||||||
|
|
||||||
|
char c;
|
||||||
|
ASSERT_TRUE(LoggingReadFile(child.stdout_read_handle(), &c, sizeof(c)));
|
||||||
|
ASSERT_EQ(' ', c);
|
||||||
|
|
||||||
|
ScopedProcessSuspend suspend(child.process_handle());
|
||||||
|
|
||||||
|
ProcessSnapshotWin process_snapshot;
|
||||||
|
ASSERT_TRUE(process_snapshot.Initialize(child.process_handle(),
|
||||||
|
ProcessSuspensionState::kSuspended));
|
||||||
|
|
||||||
|
ASSERT_GE(process_snapshot.Modules().size(), 2u);
|
||||||
|
|
||||||
|
UUID uuid;
|
||||||
|
DWORD age;
|
||||||
|
std::string pdbname;
|
||||||
|
const std::string suffix(".pdb");
|
||||||
|
|
||||||
|
// Check the main .exe to see that we can retrieve its sections.
|
||||||
|
auto module = reinterpret_cast<const internal::ModuleSnapshotWin*>(
|
||||||
|
process_snapshot.Modules()[0]);
|
||||||
|
ASSERT_TRUE(module->pe_image_reader().DebugDirectoryInformation(
|
||||||
|
&uuid, &age, &pdbname));
|
||||||
|
EXPECT_NE(std::string::npos,
|
||||||
|
pdbname.find("crashpad_snapshot_test_image_reader"));
|
||||||
|
EXPECT_EQ(
|
||||||
|
0,
|
||||||
|
pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix));
|
||||||
|
|
||||||
|
// Check the dll it loads too.
|
||||||
|
module = reinterpret_cast<const internal::ModuleSnapshotWin*>(
|
||||||
|
process_snapshot.Modules().back());
|
||||||
|
ASSERT_TRUE(module->pe_image_reader().DebugDirectoryInformation(
|
||||||
|
&uuid, &age, &pdbname));
|
||||||
|
EXPECT_NE(std::string::npos,
|
||||||
|
pdbname.find("crashpad_snapshot_test_image_reader_module"));
|
||||||
|
EXPECT_EQ(
|
||||||
|
0,
|
||||||
|
pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix));
|
||||||
|
|
||||||
|
// Tell the child it can terminate.
|
||||||
|
SetEvent(done.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ProcessSnapshotTest, CrashpadInfoChild) {
|
||||||
|
TestImageReaderChild(FILE_PATH_LITERAL("."));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(ARCH_CPU_64_BITS)
|
||||||
|
TEST(ProcessSnapshotTest, CrashpadInfoChildWOW64) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
TestImageReaderChild(FILE_PATH_LITERAL("..\\..\\out\\Debug"));
|
||||||
|
#else
|
||||||
|
TestImageReaderChild(FILE_PATH_LITERAL("..\\..\\out\\Release"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
@ -17,48 +17,11 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "test/win/child_launcher.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Ref: http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
|
|
||||||
void AppendCommandLineArgument(const std::wstring& argument,
|
|
||||||
std::wstring* command_line) {
|
|
||||||
// Don't bother quoting if unnecessary.
|
|
||||||
if (!argument.empty() &&
|
|
||||||
argument.find_first_of(L" \t\n\v\"") == std::wstring::npos) {
|
|
||||||
command_line->append(argument);
|
|
||||||
} else {
|
|
||||||
command_line->push_back(L'"');
|
|
||||||
for (std::wstring::const_iterator i = argument.begin();; ++i) {
|
|
||||||
size_t backslash_count = 0;
|
|
||||||
while (i != argument.end() && *i == L'\\') {
|
|
||||||
++i;
|
|
||||||
++backslash_count;
|
|
||||||
}
|
|
||||||
if (i == argument.end()) {
|
|
||||||
// Escape all backslashes, but let the terminating double quotation mark
|
|
||||||
// we add below be interpreted as a metacharacter.
|
|
||||||
command_line->append(backslash_count * 2, L'\\');
|
|
||||||
break;
|
|
||||||
} else if (*i == L'"') {
|
|
||||||
// Escape all backslashes and the following double quotation mark.
|
|
||||||
command_line->append(backslash_count * 2 + 1, L'\\');
|
|
||||||
command_line->push_back(*i);
|
|
||||||
} else {
|
|
||||||
// Backslashes aren't special here.
|
|
||||||
command_line->append(backslash_count, L'\\');
|
|
||||||
command_line->push_back(*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
command_line->push_back(L'"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
struct MultiprocessInfo {
|
struct MultiprocessInfo {
|
||||||
|
@ -51,6 +51,8 @@
|
|||||||
'scoped_temp_dir.h',
|
'scoped_temp_dir.h',
|
||||||
'scoped_temp_dir_posix.cc',
|
'scoped_temp_dir_posix.cc',
|
||||||
'scoped_temp_dir_win.cc',
|
'scoped_temp_dir_win.cc',
|
||||||
|
'win/child_launcher.cc',
|
||||||
|
'win/child_launcher.h',
|
||||||
'win/win_child_process.cc',
|
'win/win_child_process.cc',
|
||||||
'win/win_child_process.h',
|
'win/win_child_process.h',
|
||||||
'win/win_multiprocess.cc',
|
'win/win_multiprocess.cc',
|
||||||
|
114
test/win/child_launcher.cc
Normal file
114
test/win/child_launcher.cc
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "test/win/child_launcher.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
ChildLauncher::ChildLauncher(const std::wstring& executable,
|
||||||
|
const std::wstring& command_line)
|
||||||
|
: executable_(executable),
|
||||||
|
command_line_(command_line),
|
||||||
|
process_handle_(),
|
||||||
|
main_thread_handle_(),
|
||||||
|
stdout_read_handle_() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ChildLauncher::~ChildLauncher() {
|
||||||
|
EXPECT_EQ(WAIT_OBJECT_0,
|
||||||
|
WaitForSingleObject(process_handle_.get(), INFINITE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChildLauncher::Start() {
|
||||||
|
ASSERT_FALSE(process_handle_.is_valid());
|
||||||
|
ASSERT_FALSE(main_thread_handle_.is_valid());
|
||||||
|
ASSERT_FALSE(stdout_read_handle_.is_valid());
|
||||||
|
|
||||||
|
// Create a pipe for the stdout of the child.
|
||||||
|
SECURITY_ATTRIBUTES security_attributes = {0};
|
||||||
|
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
|
security_attributes.bInheritHandle = true;
|
||||||
|
HANDLE stdout_read;
|
||||||
|
HANDLE stdout_write;
|
||||||
|
ASSERT_TRUE(CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0));
|
||||||
|
stdout_read_handle_.reset(stdout_read);
|
||||||
|
ScopedFileHANDLE write_handle(stdout_write);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
SetHandleInformation(stdout_read_handle_.get(), HANDLE_FLAG_INHERIT, 0));
|
||||||
|
|
||||||
|
STARTUPINFO startup_info = {0};
|
||||||
|
startup_info.cb = sizeof(startup_info);
|
||||||
|
startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
startup_info.hStdOutput = write_handle.get();
|
||||||
|
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
startup_info.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
PROCESS_INFORMATION process_information;
|
||||||
|
std::wstring command_line;
|
||||||
|
AppendCommandLineArgument(executable_, &command_line);
|
||||||
|
command_line += L" ";
|
||||||
|
command_line += command_line_;
|
||||||
|
ASSERT_TRUE(CreateProcess(executable_.c_str(),
|
||||||
|
&command_line[0],
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
&startup_info,
|
||||||
|
&process_information));
|
||||||
|
// Take ownership of the two process handles returned.
|
||||||
|
main_thread_handle_.reset(process_information.hThread);
|
||||||
|
process_handle_.reset(process_information.hProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ref: http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
|
||||||
|
void AppendCommandLineArgument(const std::wstring& argument,
|
||||||
|
std::wstring* command_line) {
|
||||||
|
// Don't bother quoting if unnecessary.
|
||||||
|
if (!argument.empty() &&
|
||||||
|
argument.find_first_of(L" \t\n\v\"") == std::wstring::npos) {
|
||||||
|
command_line->append(argument);
|
||||||
|
} else {
|
||||||
|
command_line->push_back(L'"');
|
||||||
|
for (std::wstring::const_iterator i = argument.begin();; ++i) {
|
||||||
|
size_t backslash_count = 0;
|
||||||
|
while (i != argument.end() && *i == L'\\') {
|
||||||
|
++i;
|
||||||
|
++backslash_count;
|
||||||
|
}
|
||||||
|
if (i == argument.end()) {
|
||||||
|
// Escape all backslashes, but let the terminating double quotation mark
|
||||||
|
// we add below be interpreted as a metacharacter.
|
||||||
|
command_line->append(backslash_count * 2, L'\\');
|
||||||
|
break;
|
||||||
|
} else if (*i == L'"') {
|
||||||
|
// Escape all backslashes and the following double quotation mark.
|
||||||
|
command_line->append(backslash_count * 2 + 1, L'\\');
|
||||||
|
command_line->push_back(*i);
|
||||||
|
} else {
|
||||||
|
// Backslashes aren't special here.
|
||||||
|
command_line->append(backslash_count, L'\\');
|
||||||
|
command_line->push_back(*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command_line->push_back(L'"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
71
test/win/child_launcher.h
Normal file
71
test/win/child_launcher.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_
|
||||||
|
#define CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "util/win/scoped_handle.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
//! \brief Creates a child process for testing. Uses gtest `ASSERT_*` to
|
||||||
|
//! indicate failure. The child's output is passed through a pipe and is
|
||||||
|
//! available via stdout_read_handle().
|
||||||
|
class ChildLauncher {
|
||||||
|
public:
|
||||||
|
//! \brief Creates the object. \a executable will be escaped and prepended to
|
||||||
|
//! \a command_line to build the command line of the child.
|
||||||
|
ChildLauncher(const std::wstring& executable,
|
||||||
|
const std::wstring& command_line);
|
||||||
|
|
||||||
|
~ChildLauncher();
|
||||||
|
|
||||||
|
//! \brief Starts the child process, after which the handle functions below
|
||||||
|
//! will be valid.
|
||||||
|
void Start();
|
||||||
|
|
||||||
|
//! \brief The child process's `HANDLE`.
|
||||||
|
HANDLE process_handle() const { return process_handle_.get(); }
|
||||||
|
|
||||||
|
//! \brief The child process's main thread's `HANDLE`.
|
||||||
|
HANDLE main_thread_handle() const { return main_thread_handle_.get(); }
|
||||||
|
|
||||||
|
//! \brief The read end of a pipe attached to the child's stdout.
|
||||||
|
HANDLE stdout_read_handle() const { return stdout_read_handle_.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::wstring executable_;
|
||||||
|
std::wstring command_line_;
|
||||||
|
ScopedKernelHANDLE process_handle_;
|
||||||
|
ScopedKernelHANDLE main_thread_handle_;
|
||||||
|
ScopedFileHANDLE stdout_read_handle_;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \brief Utility function for building escaped command lines.
|
||||||
|
//!
|
||||||
|
//! \param[in] argument Appended to \a command_line surrounded by properly
|
||||||
|
//! escaped quotation marks, if necessary.
|
||||||
|
//! \param[inout] command_line The command line being constructed.
|
||||||
|
void AppendCommandLineArgument(const std::wstring& argument,
|
||||||
|
std::wstring* command_line);
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_
|
@ -15,13 +15,13 @@
|
|||||||
#include "util/win/process_info.h"
|
#include "util/win/process_info.h"
|
||||||
|
|
||||||
#include <imagehlp.h>
|
#include <imagehlp.h>
|
||||||
#include <rpc.h>
|
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "test/paths.h"
|
#include "test/paths.h"
|
||||||
|
#include "test/win/child_launcher.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
#include "util/misc/uuid.h"
|
#include "util/misc/uuid.h"
|
||||||
#include "util/win/scoped_handle.h"
|
#include "util/win/scoped_handle.h"
|
||||||
@ -106,11 +106,8 @@ TEST(ProcessInfo, Self) {
|
|||||||
void TestOtherProcess(const base::string16& directory_modification) {
|
void TestOtherProcess(const base::string16& directory_modification) {
|
||||||
ProcessInfo process_info;
|
ProcessInfo process_info;
|
||||||
|
|
||||||
::UUID system_uuid;
|
UUID started_uuid(UUID::InitializeWithNewTag{});
|
||||||
ASSERT_EQ(RPC_S_OK, UuidCreate(&system_uuid));
|
UUID done_uuid(UUID::InitializeWithNewTag{});
|
||||||
UUID started_uuid(reinterpret_cast<const uint8_t*>(&system_uuid.Data1));
|
|
||||||
ASSERT_EQ(RPC_S_OK, UuidCreate(&system_uuid));
|
|
||||||
UUID done_uuid(reinterpret_cast<const uint8_t*>(&system_uuid.Data1));
|
|
||||||
|
|
||||||
ScopedKernelHANDLE started(
|
ScopedKernelHANDLE started(
|
||||||
CreateEvent(nullptr, true, false, started_uuid.ToString16().c_str()));
|
CreateEvent(nullptr, true, false, started_uuid.ToString16().c_str()));
|
||||||
@ -128,30 +125,15 @@ void TestOtherProcess(const base::string16& directory_modification) {
|
|||||||
L"_process_info_test_child.exe")
|
L"_process_info_test_child.exe")
|
||||||
.value();
|
.value();
|
||||||
// TODO(scottmg): Command line escaping utility.
|
// TODO(scottmg): Command line escaping utility.
|
||||||
std::wstring command_line = child_test_executable + L" " +
|
ChildLauncher child(
|
||||||
started_uuid.ToString16() + L" " +
|
child_test_executable,
|
||||||
done_uuid.ToString16();
|
started_uuid.ToString16() + L" " + done_uuid.ToString16());
|
||||||
STARTUPINFO startup_info = {0};
|
child.Start();
|
||||||
startup_info.cb = sizeof(startup_info);
|
|
||||||
PROCESS_INFORMATION process_information;
|
|
||||||
ASSERT_TRUE(CreateProcess(child_test_executable.c_str(),
|
|
||||||
&command_line[0],
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
false,
|
|
||||||
0,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
&startup_info,
|
|
||||||
&process_information));
|
|
||||||
// Take ownership of the two process handles returned.
|
|
||||||
ScopedKernelHANDLE process_main_thread_handle(process_information.hThread);
|
|
||||||
ScopedKernelHANDLE process_handle(process_information.hProcess);
|
|
||||||
|
|
||||||
// Wait until the test has completed initialization.
|
// Wait until the test has completed initialization.
|
||||||
ASSERT_EQ(WaitForSingleObject(started.get(), INFINITE), WAIT_OBJECT_0);
|
ASSERT_EQ(WaitForSingleObject(started.get(), INFINITE), WAIT_OBJECT_0);
|
||||||
|
|
||||||
ASSERT_TRUE(process_info.Initialize(process_information.hProcess));
|
ASSERT_TRUE(process_info.Initialize(child.process_handle()));
|
||||||
|
|
||||||
// Tell the test it's OK to shut down now that we've read our data.
|
// Tell the test it's OK to shut down now that we've read our data.
|
||||||
SetEvent(done.get());
|
SetEvent(done.get());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user