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.Collections.Generic; | |
8 using System.IO; | |
9 using System.Linq; | |
10 using Google.MsAd7.BaseImpl; | |
11 using Google.MsAd7.BaseImpl.Ad7Enumerators; | |
12 using Google.MsAd7.BaseImpl.Interfaces; | |
13 using Google.NaClVsx.DebugSupport.DWARF; | |
14 using Microsoft.VisualStudio; | |
15 using Microsoft.VisualStudio.Debugger.Interop; | |
16 using NaClVsx; | |
17 | |
18 #endregion | |
19 | |
20 namespace Google.NaClVsx.DebugSupport { | |
21 /// <summary> | |
22 /// Provides address and code look-ups specific to breakpoint operations. | |
23 /// </summary> | |
24 public class BreakpointInfo : IBreakpointInfo { | |
25 #region constants | |
26 | |
27 public const int kBindErrorWarning = 2; | |
28 | |
29 #endregion | |
30 | |
31 public BreakpointInfo(SymbolDatabase database, | |
32 ISimpleSymbolProvider symbolProvider) { | |
33 database_ = database; | |
34 symbolProvider_ = symbolProvider; | |
35 } | |
36 | |
37 #region IBreakpointInfo Members | |
38 | |
39 /// <summary> | |
40 /// This function can be called to determine what errors would occur if | |
41 /// an attempt were made to bind a breakpoint at a given location. | |
42 /// </summary> | |
43 /// <param name = "pos"> | |
44 /// The position at which a breakpoint is being considered. | |
45 /// </param> | |
46 /// <param name = "ppErrorEnum"> | |
47 /// Any errors that might be encountered if an attempt was made to set a | |
48 /// breakpoint at the current location. | |
49 /// </param> | |
50 /// <returns>Whether or not the function successfully checked for errors. | |
51 /// </returns> | |
52 public int GetBindErrors(DocumentPosition pos, | |
53 out IEnumDebugErrorBreakpoints2 ppErrorEnum) { | |
54 IEnumerable<ulong> addresses; | |
55 return GetBindAddresses(pos, out addresses, out ppErrorEnum); | |
56 } | |
57 | |
58 /// <summary> | |
59 /// This function exists separately from AddressFromPosition because the d
ebugger needs | |
60 /// different rules for matching addresses. If no address is found that m
atches "position" | |
61 /// exactly, this function will return addresses for the next appropriate
position if there | |
62 /// is one. | |
63 /// </summary> | |
64 /// <param name = "msvcPosition">The position in the source code where the u
ser is trying to | |
65 /// set a breakpoint. Since this is supplied by msvc, it's 0-indexed</par
am> | |
66 /// <param name = "outAddresses">Any memory addresses that can be appropriat
ely mapped to | |
67 /// position</param> | |
68 /// <param name = "outErrorEnum">Any errors encountered while this breakpoin
t was being set. | |
69 /// </param> | |
70 /// <returns>If the breakpoint was set successfully at the exact location re
quested, this | |
71 /// function returns VSConstants.S_OK. If the breakpoint was set in a dif
ferent location, | |
72 /// the function returns kBindErrorWarning. If the breakpoint cannot be s
et at all, it | |
73 /// returns S_FALSE.</returns> | |
74 public int GetBindAddresses(DocumentPosition msvcPosition, | |
75 out IEnumerable<ulong> outAddresses, | |
76 out IEnumDebugErrorBreakpoints2 outErrorEnum) { | |
77 var addressesBound = VSConstants.S_FALSE; | |
78 outAddresses = new List<ulong>(); | |
79 var addresses = outAddresses as List<ulong>; | |
80 outErrorEnum = new ErrorBreakpointEnumerator(); | |
81 var breakpointErrorEnum = outErrorEnum as ErrorBreakpointEnumerator; | |
82 | |
83 var fileName = GetFileName(msvcPosition); | |
84 if (fileName != null) { | |
85 var files = | |
86 database_.SourceFilesByFilename[fileName]; | |
87 | |
88 if (files.Count() >= 1) { | |
89 var file = files.First(); | |
90 // Remember the path that was passed in, since that's where | |
91 // the current debugging session knows where to find this | |
92 // file | |
93 file.CurrentAbsolutePath = msvcPosition.Path; | |
94 | |
95 // Internally, MSVC uses zero-based lines. This is in contrast | |
96 // to both DWARF and MSVC's own user interface, but whatever. | |
97 var line = | |
98 NaClSymbolProvider.GetDwarfLineIndex(msvcPosition.BeginPos.dwLine)
; | |
99 var locations = | |
100 GetBreakpointLocations(file, line); | |
101 | |
102 foreach (var loc in locations) { | |
103 var address = loc.StartAddress + symbolProvider_.GetBaseAddress(); | |
104 addresses.Add(address); | |
105 | |
106 var documentContext = new DocumentContext( | |
107 msvcPosition.Path, | |
108 NaClSymbolProvider.GetMSVCLineIndex(loc.Line), | |
109 loc.Column); | |
110 | |
111 // The check decides whether we move the breakpoint to compensate fo
r user | |
112 // error. We have to get the addresses first because they are needed
for the | |
113 // code context part of the error. | |
114 if (loc.Line != line) { | |
115 var resolution = new ErrorBreakpointResolution(); | |
116 resolution.Type = enum_BP_ERROR_TYPE.BPET_SEV_LOW | | |
117 enum_BP_ERROR_TYPE.BPET_TYPE_WARNING; | |
118 | |
119 resolution.SetLocation( | |
120 enum_BP_TYPE.BPT_CODE, address, documentContext); | |
121 resolution.Message = "Had to move breakpoint to a different line."
; | |
122 | |
123 var breakpointError = new ErrorBreakpoint(resolution); | |
124 breakpointErrorEnum.Insert(breakpointError); | |
125 } | |
126 } | |
127 | |
128 uint count; | |
129 var getCountSuccess = outErrorEnum.GetCount(out count); | |
130 if (getCountSuccess == VSConstants.S_OK && count > 0) { | |
131 addressesBound = kBindErrorWarning; | |
132 } else { | |
133 addressesBound = VSConstants.S_OK; | |
134 } | |
135 } | |
136 } | |
137 | |
138 // There are any number of reasons why this could have failed completely | |
139 if (addresses.Count() == 0) { | |
140 var resolution = new ErrorBreakpointResolution(); | |
141 resolution.Type = enum_BP_ERROR_TYPE.BPET_GENERAL_ERROR; | |
142 resolution.Message = | |
143 "Could not set a breakpoint at the requested location."; | |
144 var error = new ErrorBreakpoint(resolution); | |
145 breakpointErrorEnum.Insert(error); | |
146 addressesBound = VSConstants.S_FALSE; | |
147 } | |
148 return addressesBound; | |
149 } | |
150 | |
151 #endregion | |
152 | |
153 #region Private Implementation | |
154 | |
155 private readonly SymbolDatabase database_; | |
156 private readonly ISimpleSymbolProvider symbolProvider_; | |
157 | |
158 #endregion | |
159 | |
160 #region Private Implementation | |
161 | |
162 /// <summary> | |
163 /// This function retrieves plausible code locations for the given | |
164 /// breakpoint information. | |
165 /// </summary> | |
166 /// <param name = "file"></param> | |
167 /// <param name = "line"></param> | |
168 /// <returns></returns> | |
169 private IEnumerable<SymbolDatabase.SourceLocation> GetBreakpointLocations( | |
170 SymbolDatabase.SourceFile file, | |
171 uint line) { | |
172 IEnumerable<SymbolDatabase.SourceLocation> finalLocations = | |
173 new List<SymbolDatabase.SourceLocation>(); | |
174 | |
175 // This will match any lines of code in a compatible scope at or after the
| |
176 // line where the breakpoint was requested. | |
177 var locationGuesses = | |
178 new Dictionary<ulong, List<SymbolDatabase.SourceLocation>>(); | |
179 foreach (var loc in database_.LocationsByFile[file.Key]) { | |
180 if (IsSuitableBreakpoint(loc, line)) { | |
181 if (!locationGuesses.ContainsKey(loc.Line)) { | |
182 locationGuesses.Add( | |
183 loc.Line, new List<SymbolDatabase.SourceLocation>()); | |
184 } | |
185 locationGuesses[loc.Line].Add(loc); | |
186 } | |
187 } | |
188 | |
189 if (locationGuesses.Count() > 0) { | |
190 var locationLines = new List<ulong>(); | |
191 locationLines.AddRange(locationGuesses.Keys); | |
192 locationLines.Sort(); | |
193 finalLocations = locationGuesses[locationLines.First()]; | |
194 } | |
195 | |
196 return finalLocations; | |
197 } | |
198 | |
199 /// <summary> | |
200 /// Gets the name of hte file that contains |position|. | |
201 /// </summary> | |
202 /// <param name = "position"> | |
203 /// The position whose file name is needed. | |
204 /// </param> | |
205 /// <returns> | |
206 /// Returns the name of the file that contains |position|. | |
207 /// </returns> | |
208 private string GetFileName(DocumentPosition position) { | |
209 string rValue = null; | |
210 var fileName = Path.GetFileName(position.Path); | |
211 if (fileName != null) { | |
212 if (database_.SourceFilesByFilename.ContainsKey(fileName)) { | |
213 rValue = fileName; | |
214 } | |
215 } | |
216 return rValue; | |
217 } | |
218 | |
219 /// <summary> | |
220 /// This function checks whether a given |location| would make a | |
221 /// suitable breakpoint for a |originalLine| of code the user requested. | |
222 /// In order to qualify the location must be either equal to or greater | |
223 /// than the requested line of code and must be in the same scope. | |
224 /// The caller of this function will then have to resolve the suitable | |
225 /// location that is closest to what the user requested. | |
226 /// </summary> | |
227 /// <param name = "location"> | |
228 /// A source code location to examine as a possible breakpoint location. | |
229 /// </param> | |
230 /// <param name = "originalLine"> | |
231 /// The line at which the user wants to set the breakpoint. | |
232 /// </param> | |
233 /// <returns> | |
234 /// |true| iff the location could serve as a breakpoint location. | |
235 /// </returns> | |
236 private bool IsSuitableBreakpoint(SymbolDatabase.SourceLocation location, | |
237 uint originalLine) { | |
238 var suitableBreakpoint = false; | |
239 | |
240 if (location.Line >= originalLine) { | |
241 // This could be suitable breakpoint if the current location's | |
242 // line is in the same scope as the original line. | |
243 var scope = | |
244 database_.GetScopeForAddress(location.StartAddress); | |
245 | |
246 var sortedLocationsInScope = | |
247 database_.GetLocationsByLine(scope, location.StartAddress); | |
248 if (sortedLocationsInScope.First().Line <= originalLine) { | |
249 suitableBreakpoint = true; | |
250 } else if (scope.Tag == DwarfTag.DW_TAG_catch_block || | |
251 scope.Tag == DwarfTag.DW_TAG_try_block || | |
252 scope.Tag == DwarfTag.DW_TAG_lexical_block) { | |
253 // This could still be a suitable breakpoint if this scope entry | |
254 // is for a subscope of a function that begins before the | |
255 // original line. | |
256 var outerScope = scope.OuterScope; | |
257 var sortedLocationsInOuterScope = | |
258 database_.GetLocationsByLine(outerScope, location.StartAddress); | |
259 if (sortedLocationsInOuterScope.First().Line <= originalLine) { | |
260 suitableBreakpoint = true; | |
261 } | |
262 } | |
263 } | |
264 return suitableBreakpoint; | |
265 } | |
266 | |
267 #endregion | |
268 } | |
269 } | |
OLD | NEW |