OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #region | |
6 | |
7 using System; | |
8 using System.Collections.Generic; | |
9 using System.Diagnostics; | |
10 using System.IO; | |
11 using System.Linq; | |
12 using System.Xml; | |
13 using System.Xml.Linq; | |
14 using Google.MsAd7.BaseImpl; | |
15 using Google.MsAd7.BaseImpl.Ad7Enumerators; | |
16 using Google.MsAd7.BaseImpl.Interfaces; | |
17 using Microsoft.VisualStudio; | |
18 using Microsoft.VisualStudio.Debugger.Interop; | |
19 using NaClVsx.DebugHelpers; | |
20 | |
21 #endregion | |
22 | |
23 namespace Google.NaClVsx.DebugSupport { | |
24 public class ProgramNode | |
25 : IDebugProgramNode2, | |
26 IDebugProgram2, | |
27 IDebugProgramNodeAttach2 { | |
28 public ProgramNode(NaClDebugProcess process) { | |
29 // | |
30 // Program nodes get created and destroyed a lot, so best to avoid | |
31 // doing much in this constructor. | |
32 // | |
33 Trace.WriteLine("Created ProgramNode"); | |
34 | |
35 process_ = process; | |
36 | |
37 mainThread_ = new Thread(this, "main", 0); | |
38 modules_.Add(mainModule_); | |
39 } | |
40 | |
41 public INaClDebugger Dbg { | |
42 get { return dbg_; } | |
43 } | |
44 | |
45 public Guid ProgramGuid { | |
46 get { return programGuid_; } | |
47 } | |
48 | |
49 public List<Thread> Threads { | |
50 get { return threads_; } | |
51 } | |
52 | |
53 public List<Module> Modules { | |
54 get { return modules_; } | |
55 } | |
56 | |
57 public string Arch { | |
58 get { return arch_; } | |
59 } | |
60 | |
61 public Thread MainThread { | |
62 get { return mainThread_; } | |
63 } | |
64 | |
65 public Module MainModule { | |
66 get { return mainModule_; } | |
67 } | |
68 | |
69 public Engine Engine { | |
70 get { return engine_; } | |
71 } | |
72 | |
73 public void AttachEngine(Engine eng) { | |
74 Debug.WriteLine("ProgramNode.AttachEngine"); | |
75 engine_ = eng; | |
76 | |
77 | |
78 dbg_.Stopped += OnStopEvent; | |
79 dbg_.Continuing += OnContinueEvent; | |
80 dbg_.StepFinished += OnStepFinished; | |
81 dbg_.ModuleLoaded += OnModuleLoaded; | |
82 | |
83 dbg_.Open(process_.NaClPort.ConnectionString); | |
84 | |
85 var tids = dbg_.GetThreads(); | |
86 foreach (var tid in tids) { | |
87 threads_.Add( | |
88 new Thread( | |
89 this, | |
90 String.Format("Thread {0}", tid), | |
91 tid)); | |
92 } | |
93 | |
94 SendEvent( | |
95 null, | |
96 new Ad7Events.DebugProgramCreateEvent( | |
97 enum_EVENTATTRIBUTES.EVENT_ASYNCHRONOUS)); | |
98 } | |
99 | |
100 #region IDebugProgram2 Members | |
101 | |
102 public int EnumThreads(out IEnumDebugThreads2 ppEnum) { | |
103 Debug.WriteLine("ProgramNode.EnumThreads"); | |
104 | |
105 ppEnum = new ThreadEnumerator(threads_.ConvertAll(t => (IDebugThread2) t))
; | |
106 return VSConstants.S_OK; | |
107 } | |
108 | |
109 public int GetName(out string pbstrName) { | |
110 // (ilewis) commenting out the debug spew for this function | |
111 // because it's called with ludicrous frequency. | |
112 // Debug.WriteLine("ProgramNode.GetName"); | |
113 | |
114 pbstrName = process_.ImagePath; | |
115 return VSConstants.S_OK; | |
116 } | |
117 | |
118 public int GetProcess(out IDebugProcess2 ppProcess) { | |
119 Debug.WriteLine("ProgramNode.GetProcess"); | |
120 | |
121 // NOTE: not currently called by VS debugger | |
122 throw new NotImplementedException(); | |
123 } | |
124 | |
125 public int Terminate() { | |
126 Debug.WriteLine("ProgramNode.Terminate"); | |
127 // Since we have only one program per process, we | |
128 // don't do anything here. The DebugProcess instance | |
129 // will kill the process. | |
130 SendEvent(MainThread, new Ad7Events.DebugProgramDestroyEvent(0)); | |
131 return VSConstants.S_OK; | |
132 } | |
133 | |
134 public int Attach(IDebugEventCallback2 pCallback) { | |
135 Debug.WriteLine("ProgramNode.Attach"); | |
136 // NOTE: not currently called by VS debugger | |
137 throw new NotImplementedException(); | |
138 } | |
139 | |
140 public int CanDetach() { | |
141 Debug.WriteLine("ProgramNode.CanDetach"); | |
142 return VSConstants.S_OK; | |
143 } | |
144 | |
145 public int Detach() { | |
146 Debug.WriteLine("ProgramNode.Detach"); | |
147 | |
148 dbg_.Close(); | |
149 SendEvent(null, new Ad7Events.DebugProgramDestroyEvent(0)); | |
150 return VSConstants.S_OK; | |
151 } | |
152 | |
153 public int GetProgramId(out Guid pguidProgramId) { | |
154 Debug.WriteLine("ProgramNode.GetProgramId"); | |
155 pguidProgramId = programGuid_; | |
156 return VSConstants.S_OK; | |
157 } | |
158 | |
159 public int GetDebugProperty(out IDebugProperty2 ppProperty) { | |
160 Debug.WriteLine("ProgramNode.GetDebugProperty"); | |
161 throw new NotImplementedException(); | |
162 } | |
163 | |
164 public int Execute() { | |
165 Debug.WriteLine("ProgramNode.Execute"); | |
166 dbg_.Continue(); | |
167 return VSConstants.S_OK; | |
168 } | |
169 | |
170 public int Continue(IDebugThread2 pThread) { | |
171 Debug.WriteLine("ProgramNode.Continue"); | |
172 dbg_.Continue(); | |
173 return VSConstants.S_OK; | |
174 } | |
175 | |
176 public int Step(IDebugThread2 pThread, | |
177 enum_STEPKIND sk, | |
178 enum_STEPUNIT Step) { | |
179 uint id; | |
180 if (pThread.GetThreadId(out id) != VSConstants.S_OK) { | |
181 id = 0; | |
182 } | |
183 var thread = (Thread) pThread; | |
184 var stack = thread.GetStack(); | |
185 var addr = stack[1].ReturnAddress; | |
186 var rip = stack[0].ReturnAddress; | |
187 | |
188 switch (sk) { | |
189 /* | |
190 * In the STEP OUT case, we want to break when the IP hits | |
191 * the return address on the stack. | |
192 */ | |
193 case enum_STEPKIND.STEP_OUT: | |
194 dbg_.AddTempBreakpoint(addr); | |
195 dbg_.Continue(); | |
196 break; | |
197 | |
198 /* | |
199 * In the STEP INTO case, we want to single step until the line | |
200 * number changes. | |
201 * TODO(noelallen): This is over simplified, and should be fixed | |
202 */ | |
203 case enum_STEPKIND.STEP_INTO: | |
204 dbg_.Step(id); | |
205 break; | |
206 | |
207 /* | |
208 * In the STEP OVER case, we want to break on any line number | |
209 * change in the same function, or if we return, so we add | |
210 * breakpoints for all lines. | |
211 * TODO(noelallen): This is over simplified, and should be fixed | |
212 */ | |
213 case enum_STEPKIND.STEP_OVER: | |
214 // Add the 'return' case | |
215 dbg_.AddTempBreakpoint(addr); | |
216 DocumentPosition oldPos = null; | |
217 var addrs = dbg_.Symbols.GetAddressesInScope(rip); | |
218 foreach (var curraddr in addrs) { | |
219 var newPos = dbg_.Symbols.PositionFromAddress(curraddr); | |
220 if (newPos != oldPos) { | |
221 dbg_.AddTempBreakpoint(curraddr); | |
222 } | |
223 newPos = oldPos; | |
224 } | |
225 | |
226 dbg_.Continue(); | |
227 break; | |
228 } | |
229 return VSConstants.S_OK; | |
230 } | |
231 | |
232 public int CauseBreak() { | |
233 Debug.WriteLine("ProgramNode.CauseBreak"); | |
234 dbg_.Break(); | |
235 return VSConstants.S_OK; | |
236 } | |
237 | |
238 public int EnumCodeContexts(IDebugDocumentPosition2 pDocPos, | |
239 out IEnumDebugCodeContexts2 ppEnum) { | |
240 Debug.WriteLine("ProgramNode.EnumCodeContexts"); | |
241 throw new NotImplementedException(); | |
242 } | |
243 | |
244 public int GetMemoryBytes(out IDebugMemoryBytes2 ppMemoryBytes) { | |
245 Debug.WriteLine("ProgramNode.GetMemoryBytes"); | |
246 // TODO: replace hardcoded values with something real | |
247 ppMemoryBytes = new MemoryBytes(dbg_, 0, 65536); | |
248 return VSConstants.S_OK; | |
249 } | |
250 | |
251 public int GetDisassemblyStream( | |
252 enum_DISASSEMBLY_STREAM_SCOPE dwScope, | |
253 IDebugCodeContext2 pCodeContext, | |
254 out IDebugDisassemblyStream2 ppDisassemblyStream) { | |
255 Debug.WriteLine("ProgramNode.GetDisassemblyStream"); | |
256 throw new NotImplementedException(); | |
257 } | |
258 | |
259 public int EnumModules(out IEnumDebugModules2 ppEnum) { | |
260 Debug.WriteLine("ProgramNode.EnumModules"); | |
261 | |
262 ppEnum = new ModuleEnumerator(modules_.ConvertAll(m => (IDebugModule2) m))
; | |
263 return VSConstants.S_OK; | |
264 } | |
265 | |
266 public int GetENCUpdate(out object ppUpdate) { | |
267 Debug.WriteLine("ProgramNode.GetENCUpdate"); | |
268 throw new NotImplementedException(); | |
269 } | |
270 | |
271 public int EnumCodePaths(string pszHint, | |
272 IDebugCodeContext2 pStart, | |
273 IDebugStackFrame2 pFrame, | |
274 int fSource, | |
275 out IEnumCodePaths2 ppEnum, | |
276 out IDebugCodeContext2 ppSafety) { | |
277 Debug.WriteLine("ProgramNode.EnumCodePaths"); | |
278 throw new NotImplementedException(); | |
279 } | |
280 | |
281 public int WriteDump(enum_DUMPTYPE DUMPTYPE, string pszDumpUrl) { | |
282 Debug.WriteLine("ProgramNode.WriteDump"); | |
283 throw new NotImplementedException(); | |
284 } | |
285 | |
286 #endregion | |
287 | |
288 #region IDebugProgramNode2 Members | |
289 | |
290 public int GetProgramName(out string pbstrProgramName) { | |
291 Debug.WriteLine("ProgramNode.GetProgramName"); | |
292 pbstrProgramName = null; | |
293 return VSConstants.S_OK; | |
294 } | |
295 | |
296 public int GetHostName(enum_GETHOSTNAME_TYPE dwHostNameType, | |
297 out string pbstrHostName) { | |
298 Debug.WriteLine("ProgramNode.GetHostName"); | |
299 pbstrHostName = null; | |
300 return VSConstants.S_OK; | |
301 } | |
302 | |
303 public int GetHostPid(AD_PROCESS_ID[] pHostProcessId) { | |
304 Debug.WriteLine("ProgramNode.GetHostPid"); | |
305 return process_.GetPhysicalProcessId(pHostProcessId); | |
306 } | |
307 | |
308 public int GetEngineInfo(out string pbstrEngine, out Guid pguidEngine) { | |
309 Debug.WriteLine("ProgramNode.GetEngineInfo"); | |
310 pbstrEngine = Engine.kName; | |
311 pguidEngine = new Guid(Engine.kId); | |
312 return VSConstants.S_OK; | |
313 } | |
314 | |
315 public int GetHostMachineName_V7(out string pbstrHostMachineName) { | |
316 Debug.WriteLine("ProgramNode.GetHostMachineName_V7"); | |
317 pbstrHostMachineName = null; | |
318 return VSConstants.E_NOTIMPL; | |
319 } | |
320 | |
321 public int Attach_V7(IDebugProgram2 pMDMProgram, | |
322 IDebugEventCallback2 pCallback, | |
323 uint dwReason) { | |
324 Debug.WriteLine("ProgramNode.Attach_V7"); | |
325 return VSConstants.E_NOTIMPL; | |
326 } | |
327 | |
328 public int DetachDebugger_V7() { | |
329 Debug.WriteLine("ProgramNode.DetachDebugger_V7"); | |
330 return VSConstants.E_NOTIMPL; | |
331 } | |
332 | |
333 #endregion | |
334 | |
335 #region Implementation of IDebugProgramNodeAttach2 | |
336 | |
337 public int OnAttach(ref Guid guidProgramId) { | |
338 Debug.WriteLine("ProgramNode.OnAttach"); | |
339 programGuid_ = guidProgramId; | |
340 return VSConstants.S_OK; | |
341 } | |
342 | |
343 #endregion | |
344 | |
345 #region Private Implementation | |
346 | |
347 private readonly INaClDebugger dbg_ = new NaClDebugger(); | |
348 | |
349 private readonly Module mainModule_ = new Module(); | |
350 private readonly Thread mainThread_; | |
351 private readonly List<Module> modules_ = new List<Module>(); | |
352 private readonly NaClDebugProcess process_; | |
353 | |
354 private readonly Dictionary<int, string> sourceIdToPath_ = | |
355 new Dictionary<int, string>(); | |
356 | |
357 private readonly List<Thread> threads_ = new List<Thread>(); | |
358 | |
359 // TODO(ilewis): get the base address from the debuggee instead | |
360 // of hardcoding it | |
361 | |
362 private string arch_; | |
363 private Engine engine_; | |
364 | |
365 private Dictionary<string, int> pathToSourceId_ = | |
366 new Dictionary<string, int>(); | |
367 | |
368 private Guid programGuid_ = Guid.NewGuid(); | |
369 | |
370 #endregion | |
371 | |
372 #region Private Implementation | |
373 | |
374 private void OnBreak(GdbProxy.ResultCode status, string msg, byte[] data) { | |
375 SendEvent(mainThread_, new Ad7Events.DebugBreakEvent()); | |
376 } | |
377 | |
378 void OnContinueEvent(ISimpleDebugger sender, | |
379 SimpleDebuggerTypes.EventType t, | |
380 SimpleDebuggerTypes.ResultCode status) { | |
381 Debug.WriteLine("Debug event: Continue"); | |
382 } | |
383 | |
384 private void OnModuleLoaded(ISimpleDebugger sender, | |
385 string modulepath, | |
386 string status) { | |
387 Debug.WriteLine(string.Format("{0}: {1}", modulepath, status)); | |
388 mainModule_.Url = modulepath; | |
389 mainModule_.Name = Path.GetFileName(modulepath); | |
390 mainModule_.UrlSymbolLocation = modulepath; | |
391 | |
392 SendEvent( | |
393 null, | |
394 new Ad7Events.DebugModuleLoadEvent( | |
395 mainModule_, | |
396 "Loading module", | |
397 true)); | |
398 | |
399 SendEvent( | |
400 null, | |
401 new Ad7Events.DebugSymbolSearchEvent( | |
402 mainModule_, | |
403 "DWARF symbols loaded", | |
404 enum_MODULE_INFO_FLAGS. | |
405 MIF_SYMBOLS_LOADED)); | |
406 } | |
407 | |
408 void OnStepFinished(ISimpleDebugger sender, | |
409 SimpleDebuggerTypes.EventType t, | |
410 SimpleDebuggerTypes.ResultCode status) { | |
411 SendEvent(mainThread_, new Ad7Events.DebugStepCompleteEvent()); | |
412 } | |
413 | |
414 void OnStopEvent(ISimpleDebugger sender, | |
415 SimpleDebuggerTypes.EventType eventType, | |
416 SimpleDebuggerTypes.ResultCode status) { | |
417 Debug.WriteLine("Debug event: Break"); | |
418 engine_.EnableAllBreakpoints(false); | |
419 SendEvent(mainThread_, new Ad7Events.DebugBreakEvent()); | |
420 } | |
421 | |
422 private void ParseArchString(string msg) { | |
423 Debug.WriteLine(msg); | |
424 try { | |
425 var targetString = XElement.Parse(msg); | |
426 var archElements = | |
427 targetString.Descendants("architecture"); | |
428 var el = archElements.FirstOrDefault(); | |
429 arch_ = el.Value; | |
430 } | |
431 catch (XmlException e) { | |
432 Debug.WriteLine(e.Message); | |
433 } | |
434 } | |
435 | |
436 private void ParseThreadsString(string msg) { | |
437 Debug.WriteLine(msg); | |
438 try { | |
439 var threadsString = XElement.Parse(msg); | |
440 foreach (var el in threadsString.Descendants("thread")) { | |
441 var tidStr = el.Attribute("id").Value; | |
442 var tid = Convert.ToUInt32(tidStr, 16); | |
443 | |
444 threads_.Add( | |
445 new Thread( | |
446 this, | |
447 String.Format("Thread {0}", tid), | |
448 tid)); | |
449 } | |
450 } | |
451 catch (Exception e) { | |
452 Debug.WriteLine(e.Message); | |
453 } | |
454 } | |
455 | |
456 #endregion | |
457 | |
458 internal void SendEvent(IDebugThread2 thread, Ad7Events.DebugEvent evt) { | |
459 engine_.SendEvent(this, thread, evt); | |
460 } | |
461 } | |
462 } | |
OLD | NEW |