forked from xquintana/DumpReport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLogManager.cs
304 lines (281 loc) · 13.5 KB
/
LogManager.cs
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DumpReport
{
/// <summary>
/// Holds the information about the exception found in the dump, provided that
/// there is one and it could be retrieved.
/// </summary>
public class ExceptionInfo
{
public ExceptionInfo() { threadNum = -1; }
public bool Found()
{
return ((description != null && description.Length > 0) || (module != null && module.Length > 0) ||
(frame != null && frame.Length > 0) || address > 0 || threadNum >= 0);
}
public string description;
public string module;
public string frame;
public UInt64 address;
public int threadNum;
}
/// <summary>
/// Reads the debugger's output log and gets it parsed by the Parser objects.
/// Finally, it sends the extracted information to the Report object.
/// </summary>
class LogManager
{
// Sections in the debugger's output file
public const string SECTION_MARK = ">>> ";
public const string TARGET_INFO = ">>> TARGET INFO";
public const string MANAGED_THREADS = ">>> MANAGED THREADS";
public const string MANAGED_STACKS = ">>> MANAGED STACKS";
public const string EXCEPTION_INFO = ">>> EXCEPTION INFO";
public const string HEAP = ">>> HEAP";
public const string INSTRUCT_PTRS = ">>> INSTRUCTION POINTERS";
public const string THREAD_STACKS = ">>> THREAD STACKS";
public const string LOADED_MODULES = ">>> LOADED MODULES";
public const string END_OF_LOG = ">>> END OF LOG";
// Parser objects
DumpInfoParser dumpInfoParser = new DumpInfoParser();
TargetInfoParser targetInfoParser = new TargetInfoParser();
ManagedThreadsParser managedThreadsParser = new ManagedThreadsParser();
ManagedStacksParser managedStacksParser = new ManagedStacksParser();
ExcepInfoParser excepInfoParser = new ExcepInfoParser();
HeapParser heapParser = new HeapParser();
InstPtrParser instPtrParser = new InstPtrParser();
ThreadParser threadParser = new ThreadParser();
ModuleParser moduleParser = new ModuleParser();
Dictionary<string, Parser> m_parsers = new Dictionary<string, Parser>(); // Relates a Parser object with its section mark in the debugger's log
public List<string> notes = new List<string>(); // Set of messages to be noted in the report
ExceptionInfo exceptionInfo = new ExceptionInfo();
Report report = null; // Outputs extracted data to an HTML file
Config config = null; // Stores the paramaters of the application
public LogManager(Config config, Report report)
{
this.config = config;
this.report = report;
}
// Maps the sections in the debugger's output file with the corresponding Parser object
void MapParsers()
{
m_parsers.Add(String.Empty, dumpInfoParser);
m_parsers.Add(TARGET_INFO, targetInfoParser);
m_parsers.Add(MANAGED_THREADS, managedThreadsParser);
m_parsers.Add(MANAGED_STACKS, managedStacksParser);
m_parsers.Add(EXCEPTION_INFO, excepInfoParser);
m_parsers.Add(HEAP, heapParser);
m_parsers.Add(INSTRUCT_PTRS, instPtrParser);
m_parsers.Add(THREAD_STACKS, threadParser);
m_parsers.Add(LOADED_MODULES, moduleParser);
m_parsers.Add(END_OF_LOG, null);
}
// Returns the Parser object associated to the current section in the log
Parser CheckNewSection(string line)
{
foreach (string section in m_parsers.Keys)
{
if (section.Length == 0)
continue;
if (line.Contains(section))
return m_parsers[section];
}
return null;
}
// Reads the debugger's output log file and distributes the lines of each log section
// to the corresponding Parser object.
public void ReadLog()
{
try
{
MapParsers();
Parser parser = dumpInfoParser;
using (StreamReader file = new StreamReader(config.LogFile, Encoding.Unicode))
{
string line;
while ((line = file.ReadLine()) != null)
{
if (line.Contains(SECTION_MARK))
parser = CheckNewSection(line);
else if (parser != null)
parser.AddLine(line);
}
}
if (config.LogClean)
File.Delete(config.LogFile);
}
catch (Exception ex)
{
Program.ShowError(ex.Message);
}
}
// Once the log file has been read, parse each section.
public void ParseLog()
{
try
{
foreach (Parser parser in m_parsers.Values)
if (parser != null) parser.Parse();
CheckParserInfo();
CombineParserInfo();
}
catch (Exception ex)
{
Program.ShowError(ex.Message);
}
if (config.LogClean)
File.Delete(config.LogFile);
}
// Checks the extracted info and notifies possible anomalies
void CheckParserInfo()
{
// Check if the environment variables could be retrieved
if (targetInfoParser.Environment.Keys.Count == 0)
notes.Add("The environment variables could not be retrieved.");
// Check if some PDB files have been loaded from the expected location
bool PdbLoadedFromPath = false;
foreach (ModuleInfo module in moduleParser.Modules)
if (module.pdbPath.ToUpper().Contains(config.PdbFolder.ToUpper()))
PdbLoadedFromPath = true;
if (!PdbLoadedFromPath)
notes.Add("No PDBs loaded from " + config.PdbFolder);
}
// Complements the information of some Parsers with data from other Parsers
void CombineParserInfo()
{
// Add managed information to the thread list
foreach (ManagedStackInfo stack in managedStacksParser.Stacks)
threadParser.AddManagedInfo(stack.threadNum, stack);
// Add instruction pointers to the thread list
threadParser.SetInstructionPointers(instPtrParser.InstPtrs);
}
// Returns true if the dump contains an exception and extracts as much information of the exception as possible.
public bool GetExceptionInfo()
{
if (heapParser.ErrorDetected == true)
{
heapParser.GetExceptionInfo(exceptionInfo, threadParser);
return true;
}
excepInfoParser.GetExceptionInfo(exceptionInfo, threadParser);
if (exceptionInfo.threadNum < 0)
{
// We don't have the exception details but we can at least try to identify the faulting thread
exceptionInfo.threadNum = managedThreadsParser.GetFaultingThread(); // Look in the managed threads
if (exceptionInfo.threadNum < 0) // Look for keywords in the call stacks
exceptionInfo.threadNum = threadParser.GuessFaultingThread();
}
return (exceptionInfo.address > 0 || exceptionInfo.threadNum >= 0);
}
// Returns true if the exeption info found is not valid or incomplete
public bool NeedMoreExceptionInfo()
{
return heapParser.ErrorDetected == false && exceptionInfo.address == 0;
}
// Process a specific log file that only contains an exception record
public void ParseExceptionRecord(string file)
{
excepInfoParser.RemoveLines();
if (!File.Exists(file))
return;
using (StreamReader stream = new StreamReader(file, Encoding.Unicode))
{
string line;
while ((line = stream.ReadLine()) != null)
excepInfoParser.AddLine(line);
}
excepInfoParser.Parse();
}
// Writes all information to the report
public void WriteReport()
{
Program.WriteConsole("Creating report...");
WriteHeader();
WriteExceptionInfo();
WriteAllThreads();
report.WriteJavascript(threadParser.GetNumThreads());
}
// Writes the top part of the report
void WriteHeader()
{
report.WriteDumpInfo(dumpInfoParser);
report.WriteTargetInfo(targetInfoParser);
report.WriteModuleInfo(moduleParser.Modules);
report.WriteNotes(notes);
}
// Tries to find an exception in the dump file and writes the info to the report.
void WriteExceptionInfo()
{
if (exceptionInfo.Found() == true)
{
report.WriteSectionTitle("Exception Information");
report.WriteExceptionInfo(exceptionInfo);
if (exceptionInfo.threadNum >= 0)
report.WriteFaultingThreadInfo(threadParser.GetThread(exceptionInfo.threadNum));
}
else
report.Write("No exception found.");
}
// Write the information of all threads found in the dump file
void WriteAllThreads()
{
report.WriteSectionTitle(string.Format("All threads ({0}) grouped by call stack", threadParser.GetNumThreads()));
report.WriteAllThreadsMenu();
Dictionary<string, List<ThreadInfo>> threadGroups = threadParser.GroupThreadsByCallStack();
foreach (List<ThreadInfo> threads in threadGroups.Values)
report.WriteThreadInfo(threads);
}
// Returns a script that hopefully will retrieve the exception record
public string GetExceptionRecordScript()
{
FrameInfo frameInfo;
ThreadInfo threadInfo;
if (Program.is32bitDump)
{
// Find the exception record in the block of memory pointed by the first param of 'UnhandledExceptionFilter'
threadParser.GetFrameByKeyword("UnhandledExceptionFilter", out frameInfo, out threadInfo);
if (frameInfo != null && Utils.NotZeroAddress(frameInfo.argsToChild1))
{
System.Diagnostics.Trace.WriteLine("GetExceptionRecordScript-> " + Path.GetFileName(config.DumpFile) + " using UnhandledExceptionFilter (x86)");
return Resources.dbgUnhandledExceptionFilter32.Replace("[FIRST_PARAM]", frameInfo.argsToChild1);
}
// Find the exception record in the address pointed by the third param of 'RtlDispatchException'
threadParser.GetFrameByKeyword("RtlDispatchException", out frameInfo, out threadInfo);
if (frameInfo != null && Utils.NotZeroAddress(frameInfo.argsToChild3))
{
System.Diagnostics.Trace.WriteLine("GetExceptionRecordScript-> " + Path.GetFileName(config.DumpFile) + " using RtlDispatchException (x86)");
return Resources.dbgRtlDispatchException.Replace("[THIRD_PARAM]", frameInfo.argsToChild3);
}
}
else
{
// Find the exception record in the call stack of the 'KiUserExceptionDispatch' frame
threadParser.GetFrameByKeyword("KiUserExceptionDispatch", out frameInfo, out threadInfo);
if (frameInfo != null && Utils.NotZeroAddress(frameInfo.childSP))
{
System.Diagnostics.Trace.WriteLine("GetExceptionRecordScript-> " + Path.GetFileName(config.DumpFile) + " using KiUserExceptionDispatch");
return Resources.dbgKiUserExceptionDispatch.Replace("[CHILD_SP]", frameInfo.childSP);
}
// Find the exception record in the block of memory pointed by the fourth param of 'WerpReportFault'
threadParser.GetFrameByKeyword("WerpReportFault", out frameInfo, out threadInfo);
if (frameInfo != null && Utils.NotZeroAddress(frameInfo.argsToChild4))
{
System.Diagnostics.Trace.WriteLine("GetExceptionRecordScript-> " + Path.GetFileName(config.DumpFile) + " using WerpReportFault");
return Resources.dbgWerpReportFault64.Replace("[FOURTH_PARAM]", frameInfo.argsToChild4);
}
// Find the exception record in the address pointed by the third param of 'RtlDispatchException'
threadParser.GetFrameByKeyword("RtlDispatchException", out frameInfo, out threadInfo);
if (frameInfo != null && Utils.NotZeroAddress(frameInfo.argsToChild3))
{
System.Diagnostics.Trace.WriteLine("GetExceptionRecordScript-> " + Path.GetFileName(config.DumpFile) + " using RtlDispatchException");
return Resources.dbgRtlDispatchException.Replace("[THIRD_PARAM]", frameInfo.argsToChild3);
}
}
System.Diagnostics.Trace.WriteLine("GetExceptionRecordScript-> " + Path.GetFileName(config.DumpFile) + " No suitable method found");
return null;
}
}
}