mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-26 23:01:05 +08:00
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 <mark@chromium.org>
This commit is contained in:
parent
a392a2c3ed
commit
badfacccee
@ -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*
|
||||
//!
|
||||
|
@ -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];
|
||||
|
@ -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',
|
||||
|
@ -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.
|
||||
//!
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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',
|
||||
|
203
minidump/minidump_unloaded_module_writer.cc
Normal file
203
minidump/minidump_unloaded_module_writer.cc
Normal file
@ -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 <limits>
|
||||
|
||||
#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<uint32_t>(unloaded_module_snapshot.Size(),
|
||||
std::numeric_limits<uint32_t>::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<internal::MinidumpWritable*>
|
||||
MinidumpUnloadedModuleWriter::Children() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
DCHECK(name_);
|
||||
|
||||
std::vector<MinidumpWritable*> 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<UnloadedModuleSnapshot>& 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<MinidumpUnloadedModuleWriter> 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<internal::MinidumpWritable*>
|
||||
MinidumpUnloadedModuleListWriter::Children() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
std::vector<MinidumpWritable*> 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<WritableIoVec> 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
|
154
minidump/minidump_unloaded_module_writer.h
Normal file
154
minidump/minidump_unloaded_module_writer.h
Normal file
@ -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 <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<MinidumpWritable*> Children() override;
|
||||
bool WriteObject(FileWriterInterface* file_writer) override;
|
||||
|
||||
private:
|
||||
MINIDUMP_UNLOADED_MODULE unloaded_module_;
|
||||
scoped_ptr<internal::MinidumpUTF16StringWriter> 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<UnloadedModuleSnapshot>& 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<MinidumpUnloadedModuleWriter> unloaded_module);
|
||||
|
||||
protected:
|
||||
// MinidumpWritable:
|
||||
bool Freeze() override;
|
||||
size_t SizeOfObject() override;
|
||||
std::vector<MinidumpWritable*> Children() override;
|
||||
bool WriteObject(FileWriterInterface* file_writer) override;
|
||||
|
||||
// MinidumpStreamWriter:
|
||||
MinidumpStreamType StreamType() const override;
|
||||
|
||||
private:
|
||||
PointerVector<MinidumpUnloadedModuleWriter> 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_
|
162
minidump/minidump_unloaded_module_writer_test.cc
Normal file
162
minidump/minidump_unloaded_module_writer_test.cc
Normal file
@ -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<MINIDUMP_UNLOADED_MODULE_LIST>(
|
||||
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<const MINIDUMP_UNLOADED_MODULE*>(
|
||||
&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<const MINIDUMP_UNLOADED_MODULE*>(
|
||||
&unloaded_module_list[1]),
|
||||
string_file.string(),
|
||||
kModuleName));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -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<MINIDUMP_UNLOADED_MODULE_LIST>(
|
||||
const std::string& file_contents,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
|
||||
return MinidumpListAtLocationDescriptor<MinidumpUnloadedModuleListTraits>(
|
||||
file_contents, location);
|
||||
}
|
||||
|
||||
template <>
|
||||
const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
|
||||
MINIDUMP_THREAD_LIST>(const std::string& file_contents,
|
||||
|
@ -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<MINIDUMP_UNLOADED_MODULE_LIST>(
|
||||
const std::string& file_contents,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR& location);
|
||||
|
||||
template <>
|
||||
const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
|
||||
MINIDUMP_THREAD_LIST>(const std::string& file_contents,
|
||||
|
@ -186,6 +186,12 @@ std::vector<const ModuleSnapshot*> ProcessSnapshotMac::Modules() const {
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot> ProcessSnapshotMac::UnloadedModules()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<UnloadedModuleSnapshot>();
|
||||
}
|
||||
|
||||
const ExceptionSnapshot* ProcessSnapshotMac::Exception() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return exception_.get();
|
||||
|
@ -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<const ThreadSnapshot*> Threads() const override;
|
||||
std::vector<const ModuleSnapshot*> Modules() const override;
|
||||
std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
|
||||
const ExceptionSnapshot* Exception() const override;
|
||||
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
|
||||
std::vector<HandleSnapshot> Handles() const override;
|
||||
|
@ -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<const ModuleSnapshot*> ProcessSnapshotMinidump::Modules() const {
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot> 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
|
||||
|
@ -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<const ThreadSnapshot*> Threads() const override;
|
||||
std::vector<const ModuleSnapshot*> Modules() const override;
|
||||
std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
|
||||
const ExceptionSnapshot* Exception() const override;
|
||||
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
|
||||
std::vector<HandleSnapshot> Handles() const override;
|
||||
@ -94,6 +96,7 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot {
|
||||
std::vector<MINIDUMP_DIRECTORY> stream_directory_;
|
||||
std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;
|
||||
PointerVector<internal::ModuleSnapshotMinidump> modules_;
|
||||
std::vector<UnloadedModuleSnapshot> unloaded_modules_;
|
||||
MinidumpCrashpadInfo crashpad_info_;
|
||||
std::map<std::string, std::string> annotations_simple_map_;
|
||||
FileReaderInterface* file_reader_; // weak
|
||||
|
@ -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<const ModuleSnapshot*> 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<UnloadedModuleSnapshot> UnloadedModules() const = 0;
|
||||
|
||||
//! \brief Returns ThreadSnapshot objects reflecting the threads (lightweight
|
||||
//! processes) existing in the snapshot process at the time of the
|
||||
//! snapshot.
|
||||
|
@ -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',
|
||||
|
@ -96,6 +96,11 @@ std::vector<const ModuleSnapshot*> TestProcessSnapshot::Modules() const {
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot> TestProcessSnapshot::UnloadedModules()
|
||||
const {
|
||||
return unloaded_modules_;
|
||||
}
|
||||
|
||||
const ExceptionSnapshot* TestProcessSnapshot::Exception() const {
|
||||
return exception_.get();
|
||||
}
|
||||
|
@ -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<const ThreadSnapshot*> Threads() const override;
|
||||
std::vector<const ModuleSnapshot*> Modules() const override;
|
||||
std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
|
||||
const ExceptionSnapshot* Exception() const override;
|
||||
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
|
||||
std::vector<HandleSnapshot> Handles() const override;
|
||||
@ -157,6 +168,7 @@ class TestProcessSnapshot final : public ProcessSnapshot {
|
||||
scoped_ptr<SystemSnapshot> system_;
|
||||
PointerVector<ThreadSnapshot> threads_;
|
||||
PointerVector<ModuleSnapshot> modules_;
|
||||
std::vector<UnloadedModuleSnapshot> unloaded_modules_;
|
||||
scoped_ptr<ExceptionSnapshot> exception_;
|
||||
PointerVector<MemoryMapRegionSnapshot> memory_map_;
|
||||
std::vector<HandleSnapshot> handles_;
|
||||
|
33
snapshot/unloaded_module_snapshot.cc
Normal file
33
snapshot/unloaded_module_snapshot.cc
Normal file
@ -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
|
61
snapshot/unloaded_module_snapshot.h
Normal file
61
snapshot/unloaded_module_snapshot.h
Normal file
@ -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 <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
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_
|
@ -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)
|
||||
|
@ -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<const ModuleSnapshot*> ProcessSnapshotWin::Modules() const {
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot> 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<Traits>* unload_event_trace_address =
|
||||
RtlGetUnloadEventTrace<Traits>();
|
||||
WinVMAddress address_in_target_process =
|
||||
reinterpret_cast<WinVMAddress>(unload_event_trace_address);
|
||||
|
||||
std::vector<RTL_UNLOAD_EVENT_TRACE<Traits>> 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<Traits>& 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;
|
||||
|
@ -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<const ThreadSnapshot*> Threads() const override;
|
||||
std::vector<const ModuleSnapshot*> Modules() const override;
|
||||
std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
|
||||
const ExceptionSnapshot* Exception() const override;
|
||||
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
|
||||
std::vector<HandleSnapshot> 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<internal::MemorySnapshotWin> extra_memory_;
|
||||
PointerVector<internal::ThreadSnapshotWin> threads_;
|
||||
PointerVector<internal::ModuleSnapshotWin> modules_;
|
||||
std::vector<UnloadedModuleSnapshot> unloaded_modules_;
|
||||
scoped_ptr<internal::ExceptionSnapshotWin> exception_;
|
||||
PointerVector<internal::MemoryMapRegionSnapshotWin> memory_map_;
|
||||
ProcessReaderWin process_reader_;
|
||||
|
@ -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 <class Traits>
|
||||
RTL_UNLOAD_EVENT_TRACE<Traits>* RtlGetUnloadEventTrace() {
|
||||
static const auto rtl_get_unload_event_trace =
|
||||
GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlGetUnloadEventTrace);
|
||||
return reinterpret_cast<RTL_UNLOAD_EVENT_TRACE<Traits>*>(
|
||||
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<process_types::internal::Traits32>(
|
||||
@ -99,4 +109,10 @@ template NTSTATUS NtOpenThread<process_types::internal::Traits64>(
|
||||
const process_types::CLIENT_ID<process_types::internal::Traits64>*
|
||||
client_id);
|
||||
|
||||
template RTL_UNLOAD_EVENT_TRACE<process_types::internal::Traits32>*
|
||||
RtlGetUnloadEventTrace<process_types::internal::Traits32>();
|
||||
|
||||
template RTL_UNLOAD_EVENT_TRACE<process_types::internal::Traits64>*
|
||||
RtlGetUnloadEventTrace<process_types::internal::Traits64>();
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -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 <class Traits>
|
||||
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 <class Traits>
|
||||
RTL_UNLOAD_EVENT_TRACE<Traits>* RtlGetUnloadEventTrace();
|
||||
|
||||
} // namespace crashpad
|
||||
|
Loading…
x
Reference in New Issue
Block a user