mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-03-01 10:47:06 +00:00
Surfacing the process create time in google_breakpad::ProcessState
and updating minidump_stackwalk to show process uptime. I tested this with a minidump from Chrome and I got a result that is inline with what the Windows debugger is showing for that dump: minidump_stackwalk output: -------------------------- Process uptime: 601 seconds WinDBG output: -------------- Process Uptime: 0 days 0:10:01.000 I didn't update the machine readable output of minidump_stackwalk on purpose in order to avoid breaking someone that uses it. It can be added later to the machine output if needed. R=mark@chromium.org Review URL: https://breakpad.appspot.com/7754002 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1406 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
57e5b074f6
commit
63919583ba
|
@ -732,6 +732,7 @@ class MinidumpMiscInfo : public MinidumpStream {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Minidump;
|
friend class Minidump;
|
||||||
|
friend class TestMinidumpMiscInfo;
|
||||||
|
|
||||||
static const uint32_t kStreamType = MD_MISC_INFO_STREAM;
|
static const uint32_t kStreamType = MD_MISC_INFO_STREAM;
|
||||||
|
|
||||||
|
@ -902,14 +903,14 @@ class Minidump {
|
||||||
// to avoid exposing an ugly API (GetStream needs to accept a garbage
|
// to avoid exposing an ugly API (GetStream needs to accept a garbage
|
||||||
// parameter).
|
// parameter).
|
||||||
virtual MinidumpThreadList* GetThreadList();
|
virtual MinidumpThreadList* GetThreadList();
|
||||||
MinidumpModuleList* GetModuleList();
|
virtual MinidumpModuleList* GetModuleList();
|
||||||
virtual MinidumpMemoryList* GetMemoryList();
|
virtual MinidumpMemoryList* GetMemoryList();
|
||||||
MinidumpException* GetException();
|
virtual MinidumpException* GetException();
|
||||||
MinidumpAssertion* GetAssertion();
|
virtual MinidumpAssertion* GetAssertion();
|
||||||
virtual MinidumpSystemInfo* GetSystemInfo();
|
virtual MinidumpSystemInfo* GetSystemInfo();
|
||||||
MinidumpMiscInfo* GetMiscInfo();
|
virtual MinidumpMiscInfo* GetMiscInfo();
|
||||||
MinidumpBreakpadInfo* GetBreakpadInfo();
|
virtual MinidumpBreakpadInfo* GetBreakpadInfo();
|
||||||
MinidumpMemoryInfoList* GetMemoryInfoList();
|
virtual MinidumpMemoryInfoList* GetMemoryInfoList();
|
||||||
|
|
||||||
// The next set of methods are provided for users who wish to access
|
// The next set of methods are provided for users who wish to access
|
||||||
// data in minidump files directly, while leveraging the rest of
|
// data in minidump files directly, while leveraging the rest of
|
||||||
|
|
|
@ -89,6 +89,12 @@ class MinidumpProcessor {
|
||||||
// the minidump.
|
// the minidump.
|
||||||
static bool GetOSInfo(Minidump* dump, SystemInfo* info);
|
static bool GetOSInfo(Minidump* dump, SystemInfo* info);
|
||||||
|
|
||||||
|
// Populates the |process_create_time| parameter with the create time of the
|
||||||
|
// crashed process. Returns false if this information is not available in
|
||||||
|
// the minidump |dump|.
|
||||||
|
static bool GetProcessCreateTime(Minidump* dump,
|
||||||
|
uint32_t* process_create_time);
|
||||||
|
|
||||||
// Returns a textual representation of the reason that a crash occurred,
|
// Returns a textual representation of the reason that a crash occurred,
|
||||||
// if the minidump in dump was produced as a result of a crash. Returns
|
// if the minidump in dump was produced as a result of a crash. Returns
|
||||||
// an empty string if this information cannot be determined. If address
|
// an empty string if this information cannot be determined. If address
|
||||||
|
|
|
@ -97,6 +97,7 @@ class ProcessState {
|
||||||
|
|
||||||
// Accessors. See the data declarations below.
|
// Accessors. See the data declarations below.
|
||||||
uint32_t time_date_stamp() const { return time_date_stamp_; }
|
uint32_t time_date_stamp() const { return time_date_stamp_; }
|
||||||
|
uint32_t process_create_time() const { return process_create_time_; }
|
||||||
bool crashed() const { return crashed_; }
|
bool crashed() const { return crashed_; }
|
||||||
string crash_reason() const { return crash_reason_; }
|
string crash_reason() const { return crash_reason_; }
|
||||||
uint64_t crash_address() const { return crash_address_; }
|
uint64_t crash_address() const { return crash_address_; }
|
||||||
|
@ -125,6 +126,9 @@ class ProcessState {
|
||||||
// The time-date stamp of the minidump (time_t format)
|
// The time-date stamp of the minidump (time_t format)
|
||||||
uint32_t time_date_stamp_;
|
uint32_t time_date_stamp_;
|
||||||
|
|
||||||
|
// The time-date stamp when the process was created (time_t format)
|
||||||
|
uint32_t process_create_time_;
|
||||||
|
|
||||||
// True if the process crashed, false if the dump was produced outside
|
// True if the process crashed, false if the dump was produced outside
|
||||||
// of an exception handler.
|
// of an exception handler.
|
||||||
bool crashed_;
|
bool crashed_;
|
||||||
|
|
|
@ -87,6 +87,9 @@ ProcessResult MinidumpProcessor::Process(
|
||||||
}
|
}
|
||||||
process_state->time_date_stamp_ = header->time_date_stamp;
|
process_state->time_date_stamp_ = header->time_date_stamp;
|
||||||
|
|
||||||
|
bool has_process_create_time =
|
||||||
|
GetProcessCreateTime(dump, &process_state->process_create_time_);
|
||||||
|
|
||||||
bool has_cpu_info = GetCPUInfo(dump, &process_state->system_info_);
|
bool has_cpu_info = GetCPUInfo(dump, &process_state->system_info_);
|
||||||
bool has_os_info = GetOSInfo(dump, &process_state->system_info_);
|
bool has_os_info = GetOSInfo(dump, &process_state->system_info_);
|
||||||
|
|
||||||
|
@ -135,14 +138,15 @@ ProcessResult MinidumpProcessor::Process(
|
||||||
}
|
}
|
||||||
|
|
||||||
BPLOG(INFO) << "Minidump " << dump->path() << " has " <<
|
BPLOG(INFO) << "Minidump " << dump->path() << " has " <<
|
||||||
(has_cpu_info ? "" : "no ") << "CPU info, " <<
|
(has_cpu_info ? "" : "no ") << "CPU info, " <<
|
||||||
(has_os_info ? "" : "no ") << "OS info, " <<
|
(has_os_info ? "" : "no ") << "OS info, " <<
|
||||||
(breakpad_info != NULL ? "" : "no ") << "Breakpad info, " <<
|
(breakpad_info != NULL ? "" : "no ") << "Breakpad info, " <<
|
||||||
(exception != NULL ? "" : "no ") << "exception, " <<
|
(exception != NULL ? "" : "no ") << "exception, " <<
|
||||||
(module_list != NULL ? "" : "no ") << "module list, " <<
|
(module_list != NULL ? "" : "no ") << "module list, " <<
|
||||||
(threads != NULL ? "" : "no ") << "thread list, " <<
|
(threads != NULL ? "" : "no ") << "thread list, " <<
|
||||||
(has_dump_thread ? "" : "no ") << "dump thread, and " <<
|
(has_dump_thread ? "" : "no ") << "dump thread, " <<
|
||||||
(has_requesting_thread ? "" : "no ") << "requesting thread";
|
(has_requesting_thread ? "" : "no ") << "requesting thread, and " <<
|
||||||
|
(has_process_create_time ? "" : "no ") << "process create time";
|
||||||
|
|
||||||
bool interrupted = false;
|
bool interrupted = false;
|
||||||
bool found_requesting_thread = false;
|
bool found_requesting_thread = false;
|
||||||
|
@ -618,6 +622,32 @@ bool MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool MinidumpProcessor::GetProcessCreateTime(Minidump* dump,
|
||||||
|
uint32_t* process_create_time) {
|
||||||
|
assert(dump);
|
||||||
|
assert(process_create_time);
|
||||||
|
|
||||||
|
*process_create_time = 0;
|
||||||
|
|
||||||
|
MinidumpMiscInfo* minidump_misc_info = dump->GetMiscInfo();
|
||||||
|
if (!minidump_misc_info) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MDRawMiscInfo* md_raw_misc_info = minidump_misc_info->misc_info();
|
||||||
|
if (!md_raw_misc_info) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(md_raw_misc_info->flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*process_create_time = md_raw_misc_info->process_create_time;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
||||||
MinidumpException *exception = dump->GetException();
|
MinidumpException *exception = dump->GetException();
|
||||||
|
|
|
@ -66,6 +66,7 @@ class MockMinidump : public Minidump {
|
||||||
MOCK_CONST_METHOD0(header, const MDRawHeader*());
|
MOCK_CONST_METHOD0(header, const MDRawHeader*());
|
||||||
MOCK_METHOD0(GetThreadList, MinidumpThreadList*());
|
MOCK_METHOD0(GetThreadList, MinidumpThreadList*());
|
||||||
MOCK_METHOD0(GetSystemInfo, MinidumpSystemInfo*());
|
MOCK_METHOD0(GetSystemInfo, MinidumpSystemInfo*());
|
||||||
|
MOCK_METHOD0(GetMiscInfo, MinidumpMiscInfo*());
|
||||||
MOCK_METHOD0(GetBreakpadInfo, MinidumpBreakpadInfo*());
|
MOCK_METHOD0(GetBreakpadInfo, MinidumpBreakpadInfo*());
|
||||||
MOCK_METHOD0(GetException, MinidumpException*());
|
MOCK_METHOD0(GetException, MinidumpException*());
|
||||||
MOCK_METHOD0(GetAssertion, MinidumpAssertion*());
|
MOCK_METHOD0(GetAssertion, MinidumpAssertion*());
|
||||||
|
@ -126,6 +127,17 @@ class MockMinidumpMemoryRegion : public MinidumpMemoryRegion {
|
||||||
MockMemoryRegion region_;
|
MockMemoryRegion region_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A test miscelaneous info stream, just returns values from the
|
||||||
|
// MDRawMiscInfo fed to it.
|
||||||
|
class TestMinidumpMiscInfo : public MinidumpMiscInfo {
|
||||||
|
public:
|
||||||
|
explicit TestMinidumpMiscInfo(const MDRawMiscInfo& misc_info) :
|
||||||
|
MinidumpMiscInfo(NULL) {
|
||||||
|
valid_ = true;
|
||||||
|
misc_info_ = misc_info;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -135,6 +147,7 @@ using google_breakpad::CallStack;
|
||||||
using google_breakpad::CodeModule;
|
using google_breakpad::CodeModule;
|
||||||
using google_breakpad::MinidumpContext;
|
using google_breakpad::MinidumpContext;
|
||||||
using google_breakpad::MinidumpMemoryRegion;
|
using google_breakpad::MinidumpMemoryRegion;
|
||||||
|
using google_breakpad::MinidumpMiscInfo;
|
||||||
using google_breakpad::MinidumpProcessor;
|
using google_breakpad::MinidumpProcessor;
|
||||||
using google_breakpad::MinidumpSystemInfo;
|
using google_breakpad::MinidumpSystemInfo;
|
||||||
using google_breakpad::MinidumpThreadList;
|
using google_breakpad::MinidumpThreadList;
|
||||||
|
@ -402,6 +415,8 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
|
||||||
ASSERT_EQ(state.crash_address(), 0x45U);
|
ASSERT_EQ(state.crash_address(), 0x45U);
|
||||||
ASSERT_EQ(state.threads()->size(), size_t(1));
|
ASSERT_EQ(state.threads()->size(), size_t(1));
|
||||||
ASSERT_EQ(state.requesting_thread(), 0);
|
ASSERT_EQ(state.requesting_thread(), 0);
|
||||||
|
EXPECT_EQ(1171480435U, state.time_date_stamp());
|
||||||
|
EXPECT_EQ(1171480435U, state.process_create_time());
|
||||||
|
|
||||||
CallStack *stack = state.threads()->at(0);
|
CallStack *stack = state.threads()->at(0);
|
||||||
ASSERT_TRUE(stack);
|
ASSERT_TRUE(stack);
|
||||||
|
@ -529,6 +544,40 @@ TEST_F(MinidumpProcessorTest, TestThreadMissingMemory) {
|
||||||
ASSERT_EQ(kExpectedEIP, state.threads()->at(0)->frames()->at(0)->instruction);
|
ASSERT_EQ(kExpectedEIP, state.threads()->at(0)->frames()->at(0)->instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MinidumpProcessorTest, GetProcessCreateTime) {
|
||||||
|
const uint32_t kProcessCreateTime = 2000;
|
||||||
|
const uint32_t kTimeDateStamp = 5000;
|
||||||
|
MockMinidump dump;
|
||||||
|
EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
|
||||||
|
EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
|
||||||
|
|
||||||
|
// Set time of crash.
|
||||||
|
MDRawHeader fake_header;
|
||||||
|
fake_header.time_date_stamp = kTimeDateStamp;
|
||||||
|
EXPECT_CALL(dump, header()).WillRepeatedly(Return(&fake_header));
|
||||||
|
|
||||||
|
// Set process create time.
|
||||||
|
MDRawMiscInfo raw_misc_info;
|
||||||
|
memset(&raw_misc_info, 0, sizeof(raw_misc_info));
|
||||||
|
raw_misc_info.process_create_time = kProcessCreateTime;
|
||||||
|
raw_misc_info.flags1 |= MD_MISCINFO_FLAGS1_PROCESS_TIMES;
|
||||||
|
google_breakpad::TestMinidumpMiscInfo dump_misc_info(raw_misc_info);
|
||||||
|
EXPECT_CALL(dump, GetMiscInfo()).WillRepeatedly(Return(&dump_misc_info));
|
||||||
|
|
||||||
|
// No threads
|
||||||
|
MockMinidumpThreadList thread_list;
|
||||||
|
EXPECT_CALL(dump, GetThreadList()).WillOnce(Return(&thread_list));
|
||||||
|
EXPECT_CALL(thread_list, thread_count()).WillRepeatedly(Return(0));
|
||||||
|
|
||||||
|
MinidumpProcessor processor(reinterpret_cast<SymbolSupplier*>(NULL), NULL);
|
||||||
|
ProcessState state;
|
||||||
|
EXPECT_EQ(google_breakpad::PROCESS_OK, processor.Process(&dump, &state));
|
||||||
|
|
||||||
|
// Verify the time stamps.
|
||||||
|
ASSERT_EQ(kTimeDateStamp, state.time_date_stamp());
|
||||||
|
ASSERT_EQ(kProcessCreateTime, state.process_create_time());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MinidumpProcessorTest, TestThreadMissingContext) {
|
TEST_F(MinidumpProcessorTest, TestThreadMissingContext) {
|
||||||
MockMinidump dump;
|
MockMinidump dump;
|
||||||
EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
|
EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
|
||||||
|
|
|
@ -45,6 +45,7 @@ ProcessState::~ProcessState() {
|
||||||
|
|
||||||
void ProcessState::Clear() {
|
void ProcessState::Clear() {
|
||||||
time_date_stamp_ = 0;
|
time_date_stamp_ = 0;
|
||||||
|
process_create_time_ = 0;
|
||||||
crashed_ = false;
|
crashed_ = false;
|
||||||
crash_reason_.clear();
|
crash_reason_.clear();
|
||||||
crash_address_ = 0;
|
crash_address_ = 0;
|
||||||
|
|
|
@ -46,11 +46,14 @@ package google_breakpad;
|
||||||
// A proto representation of a process, in a fully-digested state.
|
// A proto representation of a process, in a fully-digested state.
|
||||||
// See src/google_breakpad/processor/process_state.h
|
// See src/google_breakpad/processor/process_state.h
|
||||||
message ProcessStateProto {
|
message ProcessStateProto {
|
||||||
// Next value: 13
|
// Next value: 14
|
||||||
|
|
||||||
// The time-date stamp of the original minidump (time_t format)
|
// The time-date stamp of the original minidump (time_t format)
|
||||||
optional int64 time_date_stamp = 1;
|
optional int64 time_date_stamp = 1;
|
||||||
|
|
||||||
|
// The time-date stamp when the process was created (time_t format)
|
||||||
|
optional int64 process_create_time = 13;
|
||||||
|
|
||||||
message Crash {
|
message Crash {
|
||||||
// The type of crash. OS- and possibly CPU- specific. For example,
|
// The type of crash. OS- and possibly CPU- specific. For example,
|
||||||
// "EXCEPTION_ACCESS_VIOLATION" (Windows), "EXC_BAD_ACCESS /
|
// "EXCEPTION_ACCESS_VIOLATION" (Windows), "EXC_BAD_ACCESS /
|
||||||
|
|
|
@ -672,6 +672,18 @@ void PrintProcessState(const ProcessState& process_state) {
|
||||||
printf("Assertion: %s\n", assertion.c_str());
|
printf("Assertion: %s\n", assertion.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compute process uptime if the process creation and crash times are
|
||||||
|
// available in the dump.
|
||||||
|
if (process_state.time_date_stamp() != 0 &&
|
||||||
|
process_state.process_create_time() != 0 &&
|
||||||
|
process_state.time_date_stamp() >= process_state.process_create_time()) {
|
||||||
|
printf("Process uptime: %d seconds\n",
|
||||||
|
process_state.time_date_stamp() -
|
||||||
|
process_state.process_create_time());
|
||||||
|
} else {
|
||||||
|
printf("Process uptime: not available\n");
|
||||||
|
}
|
||||||
|
|
||||||
// If the thread that requested the dump is known, print it first.
|
// If the thread that requested the dump is known, print it first.
|
||||||
int requesting_thread = process_state.requesting_thread();
|
int requesting_thread = process_state.requesting_thread();
|
||||||
if (requesting_thread != -1) {
|
if (requesting_thread != -1) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ CPU:
|
||||||
|
|
||||||
Crash reason:
|
Crash reason:
|
||||||
Crash address: 0x0
|
Crash address: 0x0
|
||||||
|
Process uptime: not available
|
||||||
|
|
||||||
Thread 0 (crashed)
|
Thread 0 (crashed)
|
||||||
0 libchromeshell.so!content::::CrashIntentionally [render_frame_impl.cc : 267 + 0x2]
|
0 libchromeshell.so!content::::CrashIntentionally [render_frame_impl.cc : 267 + 0x2]
|
||||||
|
|
|
@ -6,6 +6,7 @@ CPU: x86
|
||||||
|
|
||||||
Crash reason: EXCEPTION_ACCESS_VIOLATION_WRITE
|
Crash reason: EXCEPTION_ACCESS_VIOLATION_WRITE
|
||||||
Crash address: 0x45
|
Crash address: 0x45
|
||||||
|
Process uptime: 0 seconds
|
||||||
|
|
||||||
Thread 0 (crashed)
|
Thread 0 (crashed)
|
||||||
0 test_app.exe!`anonymous namespace'::CrashFunction [test_app.cc : 58 + 0x3]
|
0 test_app.exe!`anonymous namespace'::CrashFunction [test_app.cc : 58 + 0x3]
|
||||||
|
|
Loading…
Reference in a new issue