1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
diff --git a/src/google_breakpad/processor/call_stack.h b/src/google_breakpad/processor/call_stack.h
--- a/src/google_breakpad/processor/call_stack.h
+++ b/src/google_breakpad/processor/call_stack.h
@@ -62,26 +62,30 @@ class CallStack {
// Resets the CallStack to its initial empty state
void Clear();
const vector<StackFrame*>* frames() const { return &frames_; }
// Set the TID associated with this call stack.
void set_tid(uint32_t tid) { tid_ = tid; }
+ void set_last_error(uint32_t last_error) { last_error_ = last_error; }
uint32_t tid() const { return tid_; }
+ uint32_t last_error() const { return last_error_; }
private:
// Stackwalker is responsible for building the frames_ vector.
friend class Stackwalker;
// Storage for pushed frames.
vector<StackFrame*> frames_;
// The TID associated with this call stack. Default to 0 if it's not
// available.
uint32_t tid_;
+ // The last error the OS set for this thread (win32's GetLastError())
+ uint32_t last_error_;
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_PROCSSOR_CALL_STACK_H__
diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h
--- a/src/google_breakpad/processor/minidump.h
+++ b/src/google_breakpad/processor/minidump.h
@@ -279,16 +279,26 @@ class MinidumpMemoryRegion : public Mini
class MinidumpThread : public MinidumpObject {
public:
virtual ~MinidumpThread();
const MDRawThread* thread() const { return valid_ ? &thread_ : NULL; }
// GetMemory may return NULL even if the MinidumpThread is valid,
// if the thread memory cannot be read.
virtual MinidumpMemoryRegion* GetMemory();
+ // Corresponds to win32's GetLastError function, which records the last
+ // error value set by the OS for this thread. A more useful error message
+ // can be produced by passing this value to FormatMessage:
+ //
+ // https://docs.microsoft.com/windows/win32/debug/retrieving-the-last-error-code
+ //
+ // The value may also be looked up in Microsoft's System Error Codes listing:
+ //
+ // https://docs.microsoft.com/windows/win32/debug/system-error-codes
+ virtual uint32_t GetLastError();
// GetContext may return NULL even if the MinidumpThread is valid.
virtual MinidumpContext* GetContext();
// The thread ID is used to determine if a thread is the exception thread,
// so a special getter is provided to retrieve this data from the
// MDRawThread structure. Returns false if the thread ID cannot be
// determined.
virtual bool GetThreadID(uint32_t *thread_id) const;
diff --git a/src/processor/call_stack.cc b/src/processor/call_stack.cc
--- a/src/processor/call_stack.cc
+++ b/src/processor/call_stack.cc
@@ -44,11 +44,12 @@ CallStack::~CallStack() {
void CallStack::Clear() {
for (vector<StackFrame *>::const_iterator iterator = frames_.begin();
iterator != frames_.end();
++iterator) {
delete *iterator;
}
tid_ = 0;
+ last_error_ = 0;
}
} // namespace google_breakpad
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc
--- a/src/processor/minidump.cc
+++ b/src/processor/minidump.cc
@@ -1567,16 +1567,76 @@ MinidumpMemoryRegion* MinidumpThread::Ge
if (!valid_) {
BPLOG(ERROR) << "Invalid MinidumpThread for GetMemory";
return NULL;
}
return memory_;
}
+uint32_t MinidumpThread::GetLastError() {
+ if (!valid_) {
+ BPLOG(ERROR) << "Cannot retrieve GetLastError() from an invalid thread";
+ return 0;
+ }
+
+ if (!thread_.teb) {
+ BPLOG(ERROR) << "Cannot retrieve GetLastError() without a valid TEB pointer";
+ return 0;
+ }
+
+ auto memory = minidump_->GetMemoryList();
+ if (!memory) {
+ BPLOG(ERROR) << "Cannot retrieve GetLastError() without a valid memory list";
+ return 0;
+ }
+
+ auto context = GetContext();
+ if (!context) {
+ BPLOG(ERROR) << "Cannot retrieve GetLastError()'s without a valid context";
+ return 0;
+ }
+
+ uint64_t pointer_width = 0;
+ switch (context_->GetContextCPU()) {
+ case MD_CONTEXT_X86:
+ pointer_width = 4;
+ break;
+ case MD_CONTEXT_AMD64:
+ case MD_CONTEXT_ARM64:
+ pointer_width = 8;
+ break;
+ default:
+ BPLOG(ERROR) << "GetLastError() isn't implemented for this CPU type yet";
+ return 0;
+ }
+
+ auto region = memory->GetMemoryRegionForAddress(thread_.teb);
+ if (!region) {
+ BPLOG(ERROR) << "GetLastError()'s memory isn't mapped in this minidump";
+ return 0;
+ }
+
+ // The TEB is opaque but we know the value we want lives at this offset
+ // from reverse engineering.
+ uint64_t offset = pointer_width * 13;
+ uint32_t error = 0;
+ if (!region->GetMemoryAtAddress(thread_.teb + offset, &error)) {
+ BPLOG(ERROR) << "GetLastError()'s memory isn't mapped in this minidump";
+ return 0;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&error);
+ }
+
+ return error;
+}
+
+
MinidumpContext* MinidumpThread::GetContext() {
if (!valid_) {
BPLOG(ERROR) << "Invalid MinidumpThread for GetContext";
return NULL;
}
if (!context_) {
diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc
--- a/src/processor/minidump_processor.cc
+++ b/src/processor/minidump_processor.cc
@@ -301,16 +301,17 @@ ProcessResult MinidumpProcessor::Process
}
} else {
// Threads with missing CPU contexts will hit this, but
// don't abort processing the rest of the dump just for
// one bad thread.
BPLOG(ERROR) << "No stackwalker for " << thread_string;
}
stack->set_tid(thread_id);
+ stack->set_last_error(thread->GetLastError());
process_state->threads_.push_back(stack.release());
process_state->thread_memory_regions_.push_back(thread_memory);
}
if (interrupted) {
BPLOG(INFO) << "Processing interrupted for " << dump->path();
return PROCESS_SYMBOL_SUPPLIER_INTERRUPTED;
}
|