mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
win: Make reading CrashpadInfo work across bitness
R=mark@chromium.org BUG=crashpad:50 Review URL: https://codereview.chromium.org/1355503005 .
This commit is contained in:
parent
5165c48b3a
commit
bd9bc07625
@ -99,7 +99,13 @@ CrashpadInfo::CrashpadInfo()
|
||||
crashpad_handler_behavior_(TriState::kUnset),
|
||||
system_crash_reporter_forwarding_(TriState::kUnset),
|
||||
padding_0_(0),
|
||||
simple_annotations_(nullptr) {
|
||||
simple_annotations_(nullptr)
|
||||
#if !defined(NDEBUG) && defined(OS_WIN)
|
||||
,
|
||||
invalid_read_detection_(0xbadc0de)
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -121,6 +121,10 @@ struct CrashpadInfo {
|
||||
uint16_t padding_0_;
|
||||
SimpleStringDictionary* simple_annotations_; // weak
|
||||
|
||||
#if !defined(NDEBUG) && defined(OS_WIN)
|
||||
uint32_t invalid_read_detection_;
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
@ -179,6 +179,18 @@
|
||||
'NoImportLibrary': 'true',
|
||||
},
|
||||
},
|
||||
{
|
||||
'target_name': 'crashpad_snapshot_test_simple_annotations',
|
||||
'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_simple_annotations.cc',
|
||||
],
|
||||
},
|
||||
],
|
||||
}],
|
||||
],
|
||||
|
53
snapshot/win/crashpad_snapshot_test_simple_annotations.cc
Normal file
53
snapshot/win/crashpad_snapshot_test_simple_annotations.cc
Normal file
@ -0,0 +1,53 @@
|
||||
// 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 "client/crashpad_info.h"
|
||||
#include "util/file/file_io.h"
|
||||
|
||||
int wmain(int argc, wchar_t* argv[]) {
|
||||
crashpad::CrashpadInfo* crashpad_info =
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo();
|
||||
|
||||
// This is "leaked" to crashpad_info.
|
||||
crashpad::SimpleStringDictionary* simple_annotations =
|
||||
new crashpad::SimpleStringDictionary();
|
||||
simple_annotations->SetKeyValue("#TEST# pad", "break");
|
||||
simple_annotations->SetKeyValue("#TEST# key", "value");
|
||||
simple_annotations->SetKeyValue("#TEST# pad", "crash");
|
||||
simple_annotations->SetKeyValue("#TEST# x", "y");
|
||||
simple_annotations->SetKeyValue("#TEST# longer", "shorter");
|
||||
simple_annotations->SetKeyValue("#TEST# empty_value", "");
|
||||
|
||||
crashpad_info->set_simple_annotations(simple_annotations);
|
||||
|
||||
// Tell the parent that the environment has been set up.
|
||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
|
||||
char c = ' ';
|
||||
crashpad::CheckedWriteFile(out, &c, sizeof(c));
|
||||
|
||||
HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
|
||||
PCHECK(in != INVALID_HANDLE_VALUE) << "GetStdHandle";
|
||||
crashpad::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;
|
||||
}
|
@ -57,21 +57,10 @@ bool ModuleSnapshotWin::Initialize(
|
||||
|
||||
void ModuleSnapshotWin::GetCrashpadOptions(CrashpadInfoClientOptions* options) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
|
||||
process_types::CrashpadInfo crashpad_info;
|
||||
if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) {
|
||||
options->crashpad_handler_behavior = TriState::kUnset;
|
||||
options->system_crash_reporter_forwarding = TriState::kUnset;
|
||||
return;
|
||||
}
|
||||
|
||||
options->crashpad_handler_behavior =
|
||||
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
|
||||
crashpad_info.crashpad_handler_behavior);
|
||||
|
||||
options->system_crash_reporter_forwarding =
|
||||
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
|
||||
crashpad_info.system_crash_reporter_forwarding);
|
||||
if (process_reader_->Is64Bit())
|
||||
GetCrashpadOptionsInternal<process_types::internal::Traits64>(options);
|
||||
else
|
||||
GetCrashpadOptionsInternal<process_types::internal::Traits32>(options);
|
||||
}
|
||||
|
||||
std::string ModuleSnapshotWin::Name() const {
|
||||
@ -171,5 +160,24 @@ std::map<std::string, std::string> ModuleSnapshotWin::AnnotationsSimpleMap()
|
||||
return annotations_reader.SimpleMap();
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
void ModuleSnapshotWin::GetCrashpadOptionsInternal(
|
||||
CrashpadInfoClientOptions* options) {
|
||||
process_types::CrashpadInfo<Traits> crashpad_info;
|
||||
if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) {
|
||||
options->crashpad_handler_behavior = TriState::kUnset;
|
||||
options->system_crash_reporter_forwarding = TriState::kUnset;
|
||||
return;
|
||||
}
|
||||
|
||||
options->crashpad_handler_behavior =
|
||||
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
|
||||
crashpad_info.crashpad_handler_behavior);
|
||||
|
||||
options->system_crash_reporter_forwarding =
|
||||
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
|
||||
crashpad_info.system_crash_reporter_forwarding);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
@ -85,6 +85,9 @@ class ModuleSnapshotWin final : public ModuleSnapshot {
|
||||
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
|
||||
|
||||
private:
|
||||
template <class Traits>
|
||||
void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options);
|
||||
|
||||
std::wstring name_;
|
||||
time_t timestamp_;
|
||||
scoped_ptr<PEImageReader> pe_image_reader_;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "client/simple_string_dictionary.h"
|
||||
#include "snapshot/win/pe_image_reader.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "util/win/process_structs.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -34,13 +35,20 @@ PEImageAnnotationsReader::PEImageAnnotationsReader(
|
||||
|
||||
std::map<std::string, std::string> PEImageAnnotationsReader::SimpleMap() const {
|
||||
std::map<std::string, std::string> simple_map_annotations;
|
||||
ReadCrashpadSimpleAnnotations(&simple_map_annotations);
|
||||
if (process_reader_->Is64Bit()) {
|
||||
ReadCrashpadSimpleAnnotations<process_types::internal::Traits64>(
|
||||
&simple_map_annotations);
|
||||
} else {
|
||||
ReadCrashpadSimpleAnnotations<process_types::internal::Traits32>(
|
||||
&simple_map_annotations);
|
||||
}
|
||||
return simple_map_annotations;
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
void PEImageAnnotationsReader::ReadCrashpadSimpleAnnotations(
|
||||
std::map<std::string, std::string>* simple_map_annotations) const {
|
||||
process_types::CrashpadInfo crashpad_info;
|
||||
process_types::CrashpadInfo<Traits> crashpad_info;
|
||||
if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info))
|
||||
return;
|
||||
|
||||
|
@ -56,6 +56,7 @@ class PEImageAnnotationsReader {
|
||||
|
||||
private:
|
||||
// Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap().
|
||||
template <class Traits>
|
||||
void ReadCrashpadSimpleAnnotations(
|
||||
std::map<std::string, std::string>* simple_map_annotations) const;
|
||||
|
||||
|
@ -22,12 +22,15 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "client/crashpad_info.h"
|
||||
#include "client/simple_string_dictionary.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/win/pe_image_reader.h"
|
||||
#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"
|
||||
@ -44,98 +47,99 @@ enum TestType {
|
||||
kCrashDebugBreak,
|
||||
};
|
||||
|
||||
template <TestType Type>
|
||||
class TestPEImageAnnotationsReader final : public WinMultiprocess {
|
||||
public:
|
||||
TestPEImageAnnotationsReader() {}
|
||||
~TestPEImageAnnotationsReader() {}
|
||||
void TestAnnotationsOnCrash(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"_simple_annotations.exe")
|
||||
.value();
|
||||
ChildLauncher child(child_test_executable, L"");
|
||||
child.Start();
|
||||
|
||||
private:
|
||||
// WinMultiprocess:
|
||||
// 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));
|
||||
|
||||
void WinMultiprocessParent() override {
|
||||
ProcessReaderWin process_reader;
|
||||
ASSERT_TRUE(process_reader.Initialize(ChildProcess(),
|
||||
ProcessSuspensionState::kRunning));
|
||||
ProcessReaderWin process_reader;
|
||||
ASSERT_TRUE(process_reader.Initialize(child.process_handle(),
|
||||
ProcessSuspensionState::kRunning));
|
||||
|
||||
// Wait for the child process to indicate that it's done setting up its
|
||||
// annotations via the CrashpadInfo interface.
|
||||
char c;
|
||||
CheckedReadFile(ReadPipeHandle(), &c, sizeof(c));
|
||||
|
||||
// Verify the "simple map" annotations set via the CrashpadInfo interface.
|
||||
const std::vector<ProcessInfo::Module>& modules = process_reader.Modules();
|
||||
std::map<std::string, std::string> all_annotations_simple_map;
|
||||
for (const ProcessInfo::Module& module : modules) {
|
||||
PEImageReader pe_image_reader;
|
||||
pe_image_reader.Initialize(&process_reader,
|
||||
module.dll_base,
|
||||
module.size,
|
||||
base::UTF16ToUTF8(module.name));
|
||||
PEImageAnnotationsReader module_annotations_reader(
|
||||
&process_reader, &pe_image_reader, module.name);
|
||||
std::map<std::string, std::string> module_annotations_simple_map =
|
||||
module_annotations_reader.SimpleMap();
|
||||
all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
|
||||
module_annotations_simple_map.end());
|
||||
}
|
||||
|
||||
EXPECT_GE(all_annotations_simple_map.size(), 5u);
|
||||
EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]);
|
||||
EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]);
|
||||
EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]);
|
||||
EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]);
|
||||
EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]);
|
||||
|
||||
if (Type == kCrashDebugBreak)
|
||||
SetExpectedChildExitCode(STATUS_BREAKPOINT);
|
||||
|
||||
// Tell the child process to continue.
|
||||
CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
|
||||
// Verify the "simple map" annotations set via the CrashpadInfo interface.
|
||||
const std::vector<ProcessInfo::Module>& modules = process_reader.Modules();
|
||||
std::map<std::string, std::string> all_annotations_simple_map;
|
||||
for (const ProcessInfo::Module& module : modules) {
|
||||
PEImageReader pe_image_reader;
|
||||
pe_image_reader.Initialize(&process_reader,
|
||||
module.dll_base,
|
||||
module.size,
|
||||
base::UTF16ToUTF8(module.name));
|
||||
PEImageAnnotationsReader module_annotations_reader(
|
||||
&process_reader, &pe_image_reader, module.name);
|
||||
std::map<std::string, std::string> module_annotations_simple_map =
|
||||
module_annotations_reader.SimpleMap();
|
||||
all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
|
||||
module_annotations_simple_map.end());
|
||||
}
|
||||
|
||||
void WinMultiprocessChild() override {
|
||||
CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
|
||||
EXPECT_GE(all_annotations_simple_map.size(), 5u);
|
||||
EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]);
|
||||
EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]);
|
||||
EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]);
|
||||
EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]);
|
||||
EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]);
|
||||
|
||||
// This is "leaked" to crashpad_info.
|
||||
SimpleStringDictionary* simple_annotations = new SimpleStringDictionary();
|
||||
simple_annotations->SetKeyValue("#TEST# pad", "break");
|
||||
simple_annotations->SetKeyValue("#TEST# key", "value");
|
||||
simple_annotations->SetKeyValue("#TEST# pad", "crash");
|
||||
simple_annotations->SetKeyValue("#TEST# x", "y");
|
||||
simple_annotations->SetKeyValue("#TEST# longer", "shorter");
|
||||
simple_annotations->SetKeyValue("#TEST# empty_value", "");
|
||||
|
||||
crashpad_info->set_simple_annotations(simple_annotations);
|
||||
|
||||
// Tell the parent that the environment has been set up.
|
||||
char c = '\0';
|
||||
CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
|
||||
|
||||
// Wait for the parent to indicate that it's safe to continue/crash.
|
||||
CheckedReadFile(ReadPipeHandle(), &c, sizeof(c));
|
||||
|
||||
switch (Type) {
|
||||
case kDontCrash:
|
||||
break;
|
||||
|
||||
case kCrashDebugBreak:
|
||||
__debugbreak();
|
||||
break;
|
||||
}
|
||||
// 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));
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestPEImageAnnotationsReader);
|
||||
};
|
||||
EXPECT_EQ(expected_exit_code, child.WaitForExit());
|
||||
}
|
||||
|
||||
TEST(PEImageAnnotationsReader, DontCrash) {
|
||||
WinMultiprocess::Run<TestPEImageAnnotationsReader<kDontCrash>>();
|
||||
TestAnnotationsOnCrash(kDontCrash, FILE_PATH_LITERAL("."));
|
||||
}
|
||||
|
||||
TEST(PEImageAnnotationsReader, CrashDebugBreak) {
|
||||
WinMultiprocess::Run<TestPEImageAnnotationsReader<kCrashDebugBreak>>();
|
||||
TestAnnotationsOnCrash(kCrashDebugBreak, FILE_PATH_LITERAL("."));
|
||||
}
|
||||
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
TEST(PEImageAnnotationsReader, DontCrashWOW64) {
|
||||
#ifndef NDEBUG
|
||||
TestAnnotationsOnCrash(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Debug"));
|
||||
#else
|
||||
TestAnnotationsOnCrash(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Release"));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(PEImageAnnotationsReader, CrashDebugBreakWOW64) {
|
||||
#ifndef NDEBUG
|
||||
TestAnnotationsOnCrash(kCrashDebugBreak,
|
||||
FILE_PATH_LITERAL("..\\..\\out\\Debug"));
|
||||
#else
|
||||
TestAnnotationsOnCrash(kCrashDebugBreak,
|
||||
FILE_PATH_LITERAL("..\\..\\out\\Release"));
|
||||
#endif
|
||||
}
|
||||
#endif // ARCH_CPU_64_BITS
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "client/crashpad_info.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "util/misc/pdb_structures.h"
|
||||
#include "util/win/process_structs.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -34,6 +35,20 @@ std::string RangeToString(const CheckedWinAddressRange& range) {
|
||||
range.Is64Bit() ? "64" : "32");
|
||||
}
|
||||
|
||||
// Map from Traits to an IMAGE_NT_HEADERSxx.
|
||||
template <class Traits>
|
||||
struct NtHeadersForTraits;
|
||||
|
||||
template <>
|
||||
struct NtHeadersForTraits<process_types::internal::Traits32> {
|
||||
using type = IMAGE_NT_HEADERS32;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct NtHeadersForTraits<process_types::internal::Traits64> {
|
||||
using type = IMAGE_NT_HEADERS64;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
PEImageReader::PEImageReader()
|
||||
@ -65,20 +80,16 @@ bool PEImageReader::Initialize(ProcessReaderWin* process_reader,
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
bool PEImageReader::GetCrashpadInfo(
|
||||
process_types::CrashpadInfo* crashpad_info) const {
|
||||
process_types::CrashpadInfo<Traits>* crashpad_info) const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
|
||||
IMAGE_SECTION_HEADER section;
|
||||
if (process_reader_->Is64Bit()) {
|
||||
if (!GetSectionByName<IMAGE_NT_HEADERS64>("CPADinfo", §ion))
|
||||
return false;
|
||||
} else {
|
||||
if (!GetSectionByName<IMAGE_NT_HEADERS32>("CPADinfo", §ion))
|
||||
return false;
|
||||
}
|
||||
if (!GetSectionByName<NtHeadersForTraits<Traits>::type>("CPADinfo", §ion))
|
||||
return false;
|
||||
|
||||
if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo)) {
|
||||
if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo<Traits>)) {
|
||||
LOG(WARNING) << "small crashpad info section size "
|
||||
<< section.Misc.VirtualSize << ", " << module_name_;
|
||||
return false;
|
||||
@ -100,9 +111,8 @@ bool PEImageReader::GetCrashpadInfo(
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(scottmg): process_types for cross-bitness.
|
||||
if (!process_reader_->ReadMemory(crashpad_info_address,
|
||||
sizeof(process_types::CrashpadInfo),
|
||||
sizeof(process_types::CrashpadInfo<Traits>),
|
||||
crashpad_info)) {
|
||||
LOG(WARNING) << "could not read crashpad info " << module_name_;
|
||||
return false;
|
||||
@ -270,4 +280,13 @@ bool PEImageReader::CheckedReadMemory(WinVMAddress address,
|
||||
return process_reader_->ReadMemory(address, size, into);
|
||||
}
|
||||
|
||||
// Explicit instantiations with the only 2 valid template arguments to avoid
|
||||
// putting the body of the function in the header.
|
||||
template bool PEImageReader::GetCrashpadInfo<process_types::internal::Traits32>(
|
||||
process_types::CrashpadInfo<process_types::internal::Traits32>*
|
||||
crashpad_info) const;
|
||||
template bool PEImageReader::GetCrashpadInfo<process_types::internal::Traits64>(
|
||||
process_types::CrashpadInfo<process_types::internal::Traits64>*
|
||||
crashpad_info) const;
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "util/misc/uuid.h"
|
||||
#include "util/win/address_types.h"
|
||||
#include "util/win/checked_win_address_range.h"
|
||||
#include "util/win/process_structs.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -31,7 +32,7 @@ class ProcessReaderWin;
|
||||
|
||||
namespace process_types {
|
||||
|
||||
// TODO(scottmg): Genericize and/or? move process_types out of mac/.
|
||||
template <class Traits>
|
||||
struct CrashpadInfo {
|
||||
uint32_t signature;
|
||||
uint32_t size;
|
||||
@ -39,10 +40,7 @@ struct CrashpadInfo {
|
||||
uint8_t crashpad_handler_behavior; // TriState.
|
||||
uint8_t system_crash_reporter_forwarding; // TriState.
|
||||
uint16_t padding_0;
|
||||
uint64_t simple_annotations; // TODO(scottmg): x86/64.
|
||||
};
|
||||
|
||||
struct Section {
|
||||
typename Traits::Pointer simple_annotations;
|
||||
};
|
||||
|
||||
} // namespace process_types
|
||||
@ -92,7 +90,9 @@ class PEImageReader {
|
||||
//! \return `true` on success, `false` on failure. If the module does not have
|
||||
//! a `CPADinfo` section, this will return `false` without logging any
|
||||
//! messages. Other failures will result in messages being logged.
|
||||
bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const;
|
||||
template <class Traits>
|
||||
bool GetCrashpadInfo(
|
||||
process_types::CrashpadInfo<Traits>* crashpad_info) const;
|
||||
|
||||
//! \brief Obtains information from the module's debug directory, if any.
|
||||
//!
|
||||
|
@ -25,12 +25,13 @@ ChildLauncher::ChildLauncher(const std::wstring& executable,
|
||||
command_line_(command_line),
|
||||
process_handle_(),
|
||||
main_thread_handle_(),
|
||||
stdout_read_handle_() {
|
||||
stdout_read_handle_(),
|
||||
stdin_write_handle_() {
|
||||
}
|
||||
|
||||
ChildLauncher::~ChildLauncher() {
|
||||
EXPECT_EQ(WAIT_OBJECT_0,
|
||||
WaitForSingleObject(process_handle_.get(), INFINITE));
|
||||
if (process_handle_.is_valid())
|
||||
WaitForExit();
|
||||
}
|
||||
|
||||
void ChildLauncher::Start() {
|
||||
@ -38,10 +39,11 @@ void ChildLauncher::Start() {
|
||||
ASSERT_FALSE(main_thread_handle_.is_valid());
|
||||
ASSERT_FALSE(stdout_read_handle_.is_valid());
|
||||
|
||||
// Create a pipe for the stdout of the child.
|
||||
// Create pipes for the stdin/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));
|
||||
@ -50,9 +52,17 @@ void ChildLauncher::Start() {
|
||||
ASSERT_TRUE(
|
||||
SetHandleInformation(stdout_read_handle_.get(), HANDLE_FLAG_INHERIT, 0));
|
||||
|
||||
HANDLE stdin_read;
|
||||
HANDLE stdin_write;
|
||||
ASSERT_TRUE(CreatePipe(&stdin_read, &stdin_write, &security_attributes, 0));
|
||||
stdin_write_handle_.reset(stdin_write);
|
||||
ScopedFileHANDLE read_handle(stdin_read);
|
||||
ASSERT_TRUE(
|
||||
SetHandleInformation(stdin_write_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.hStdInput = read_handle.get();
|
||||
startup_info.hStdOutput = write_handle.get();
|
||||
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
startup_info.dwFlags = STARTF_USESTDHANDLES;
|
||||
@ -76,6 +86,16 @@ void ChildLauncher::Start() {
|
||||
process_handle_.reset(process_information.hProcess);
|
||||
}
|
||||
|
||||
DWORD ChildLauncher::WaitForExit() {
|
||||
EXPECT_TRUE(process_handle_.is_valid());
|
||||
EXPECT_EQ(WAIT_OBJECT_0,
|
||||
WaitForSingleObject(process_handle_.get(), INFINITE));
|
||||
DWORD exit_code = 0;
|
||||
EXPECT_TRUE(GetExitCodeProcess(process_handle_.get(), &exit_code));
|
||||
process_handle_.reset();
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
@ -26,7 +26,8 @@ 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().
|
||||
//! available via stdout_read_handle(), and the child's input is attached to
|
||||
//! a second pipe available via stdin_write_handle().
|
||||
class ChildLauncher {
|
||||
public:
|
||||
//! \brief Creates the object. \a executable will be escaped and prepended to
|
||||
@ -40,6 +41,11 @@ class ChildLauncher {
|
||||
//! will be valid.
|
||||
void Start();
|
||||
|
||||
//! \brief Waits for the child process to exit.
|
||||
//!
|
||||
//! \return The process exit code.
|
||||
DWORD WaitForExit();
|
||||
|
||||
//! \brief The child process's `HANDLE`.
|
||||
HANDLE process_handle() const { return process_handle_.get(); }
|
||||
|
||||
@ -49,12 +55,16 @@ class ChildLauncher {
|
||||
//! \brief The read end of a pipe attached to the child's stdout.
|
||||
HANDLE stdout_read_handle() const { return stdout_read_handle_.get(); }
|
||||
|
||||
//! \brief The write end of a pipe attached to the child's stdin.
|
||||
HANDLE stdin_write_handle() const { return stdin_write_handle_.get(); }
|
||||
|
||||
private:
|
||||
std::wstring executable_;
|
||||
std::wstring command_line_;
|
||||
ScopedKernelHANDLE process_handle_;
|
||||
ScopedKernelHANDLE main_thread_handle_;
|
||||
ScopedFileHANDLE stdout_read_handle_;
|
||||
ScopedFileHANDLE stdin_write_handle_;
|
||||
};
|
||||
|
||||
//! \brief Utility function for building escaped command lines.
|
||||
|
Loading…
x
Reference in New Issue
Block a user