|
1 /* |
|
2 * Copyright (c) 2004-2008 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 using System; |
|
19 using System.Collections.Generic; |
|
20 using System.Text; |
|
21 using System.IO; |
|
22 using SymbianUtils; |
|
23 using SymbianUtils.Tracer; |
|
24 using SymbianUtils.FileTypes; |
|
25 using SymbianStructuresLib.Debug.Symbols; |
|
26 using SymbianStructuresLib.CodeSegments; |
|
27 using SymbianDebugLib.Entity; |
|
28 using SymbianDebugLib.PluginAPI; |
|
29 using SymbianCodeLib.SourceManagement.Source; |
|
30 using SymbianCodeLib.SourceManagement.Provisioning; |
|
31 |
|
32 namespace SymbianCodeLib.DbgEnginePlugin |
|
33 { |
|
34 internal class CodePrimer : DbgPluginPrimer |
|
35 { |
|
36 #region Constructors |
|
37 public CodePrimer( CodePlugin aPlugin ) |
|
38 : base( aPlugin ) |
|
39 { |
|
40 } |
|
41 #endregion |
|
42 |
|
43 #region From DbgPluginPrimer |
|
44 public override void Add( DbgEntity aEntity ) |
|
45 { |
|
46 CodeSourceProvider provider = null; |
|
47 // |
|
48 if ( aEntity.FSEntity.IsFile ) |
|
49 { |
|
50 if ( aEntity.Exists && aEntity.FSEntity.IsValid ) |
|
51 { |
|
52 provider = ProvisioningManager.GetProvider( aEntity.FSEntity.FullName ); |
|
53 } |
|
54 // |
|
55 if ( provider != null ) |
|
56 { |
|
57 using ( CodeSourceCollection sources = provider.CreateSources( aEntity.FullName ) ) |
|
58 { |
|
59 // Make sure the time to read attribute is setup in alignment with |
|
60 // whether the entity was explicitly added by the user or found implicitly |
|
61 // by scanning. |
|
62 if ( aEntity.WasAddedExplicitly == false ) |
|
63 { |
|
64 foreach ( CodeSource source in sources ) |
|
65 { |
|
66 // This means, don't read this source until it is actually |
|
67 // referenced by the client. I.e. until the client activates |
|
68 // a code segment that refers to this |
|
69 source.TimeToRead = CodeSource.TTimeToRead.EReadWhenNeeded; |
|
70 } |
|
71 } |
|
72 |
|
73 // Ownership is transferred |
|
74 iSources.AddRange( sources ); |
|
75 sources.Clear(); |
|
76 } |
|
77 } |
|
78 else |
|
79 { |
|
80 throw new NotSupportedException( "Specified file type is not supported" ); |
|
81 } |
|
82 } |
|
83 else |
|
84 { |
|
85 throw new ArgumentException( "SymbianCodeLib does not support directory entities" ); |
|
86 } |
|
87 } |
|
88 |
|
89 public override void Prime( TSynchronicity aSynchronicity ) |
|
90 { |
|
91 CodePlugin plugin = this.Plugin; |
|
92 |
|
93 // Wipe any state ready for new priming attempt |
|
94 base.OnPrepareToPrime(); |
|
95 if ( base.ResetEngineBeforePriming ) |
|
96 { |
|
97 plugin.Clear(); |
|
98 } |
|
99 |
|
100 // Report the "priming started event" prior to adding the sources |
|
101 base.ReportEvent( TPrimeEvent.EEventPrimingStarted, null ); |
|
102 |
|
103 // Any sources already registered with the source manager at the start of a new |
|
104 // priming request are assumed to already be complete. |
|
105 RecordAlreadyCompletedSources(); |
|
106 |
|
107 // Initially, whilst adding the sources to the source manager, we'll |
|
108 // operate synchronously. This prevents us from triggering the read |
|
109 // operation in the SourceAdded callback below. |
|
110 iSynchronicity = TSynchronicity.ESynchronous; |
|
111 |
|
112 // Listen to source manager events in case any new sources are |
|
113 // created whilst reading those we already know about. |
|
114 CodeSourceManager sourceManager = this.SourceManager; |
|
115 sourceManager.SourceAdded += new CodeSourceManager.SourceEventHandler( SourceManager_SourceAdded ); |
|
116 sourceManager.SourceRemoved += new CodeSourceManager.SourceEventHandler( SourceManager_SourceRemoved ); |
|
117 |
|
118 // Tell the source manager which sources we are reading. This also will |
|
119 // call our event handler (added above) so that we can observe source events. |
|
120 CodeSourceCollection sources = iSources; |
|
121 iSources = new CodeSourceCollection(); |
|
122 sourceManager.AddRange( sources ); |
|
123 |
|
124 // If we're operating asynchronously, then the loop below will potentially |
|
125 // complete quite quickly. This means that any (new/additional) sources which |
|
126 // are created (asynchronously) whilst reading is underway will not themselves |
|
127 // be read. |
|
128 // |
|
129 // Therefore, we store the synchronisation mode as a data member, and when |
|
130 // 'SourceAdded' is called, if operating asynchronously, we'll also initiate |
|
131 // a read operation for the source (as soon as it is added). |
|
132 // |
|
133 // If we're operating synchronously, then this isn't important. Because we're |
|
134 // behaving synchronously, the loop below will only process one source at a |
|
135 // time (before waiting) and therefore even if that source adds new/additional |
|
136 // sources to the source manager, we'll catch them as soon as we move the |
|
137 // next iteration around the loop. |
|
138 iSynchronicity = aSynchronicity; |
|
139 |
|
140 // TODO: possibly re-write this so that it uses two separate code paths |
|
141 // for synchronous and asynchronous priming? By trying to use one path |
|
142 // this code looks rather complex and isn't terribly robust. |
|
143 try |
|
144 { |
|
145 // Now we can start the sources running. |
|
146 int count = iSourcesYetToBePrimed.Count; |
|
147 while ( count > 0 ) |
|
148 { |
|
149 // Get the head source and remove it from the pending list |
|
150 CodeSource source = iSourcesYetToBePrimed[ 0 ]; |
|
151 iSourcesYetToBePrimed.Remove( source ); |
|
152 |
|
153 // If the source wants to read it's data immediately, then activated |
|
154 // it right now... |
|
155 if ( source.TimeToRead == CodeSource.TTimeToRead.EReadWhenPriming ) |
|
156 { |
|
157 source.Read( aSynchronicity ); |
|
158 } |
|
159 else |
|
160 { |
|
161 // This source will read it's data on it's own terms so skip |
|
162 // it to ensure that we can track when all the other sources |
|
163 // (that do actually read their files now..) are ready. |
|
164 Skip( source ); |
|
165 } |
|
166 |
|
167 count = iSourcesYetToBePrimed.Count; |
|
168 } |
|
169 } |
|
170 catch( Exception e ) |
|
171 { |
|
172 // If priming failed, report completion before rethrowing... |
|
173 OnPrimeComplete(); |
|
174 throw e; |
|
175 } |
|
176 } |
|
177 |
|
178 protected override void OnPrimeComplete() |
|
179 { |
|
180 System.Diagnostics.Debug.Assert( iSourcesYetToBePrimed.Count == 0 ); |
|
181 try |
|
182 { |
|
183 SourceManager.SourceAdded -= new CodeSourceManager.SourceEventHandler( SourceManager_SourceAdded ); |
|
184 SourceManager.SourceRemoved -= new CodeSourceManager.SourceEventHandler( SourceManager_SourceRemoved ); |
|
185 // |
|
186 base.OnPrimeComplete(); |
|
187 } |
|
188 finally |
|
189 { |
|
190 SourceEventsUnsubscribe(); |
|
191 } |
|
192 } |
|
193 |
|
194 protected override int Count |
|
195 { |
|
196 get |
|
197 { |
|
198 int count = SourceManager.Count; |
|
199 return count; |
|
200 } |
|
201 } |
|
202 #endregion |
|
203 |
|
204 #region API |
|
205 #endregion |
|
206 |
|
207 #region Properties |
|
208 internal CodePlugin Plugin |
|
209 { |
|
210 get { return base.Engine as CodePlugin; } |
|
211 } |
|
212 |
|
213 internal CodeSourceManager SourceManager |
|
214 { |
|
215 get { return Plugin.SourceManager; } |
|
216 } |
|
217 |
|
218 internal CodeSourceProviderManager ProvisioningManager |
|
219 { |
|
220 get { return Plugin.ProvisioningManager; } |
|
221 } |
|
222 #endregion |
|
223 |
|
224 #region Event handlers |
|
225 private void SourceManager_SourceAdded( CodeSource aSource ) |
|
226 { |
|
227 base.Engine.Trace( "[CodePrimer] SourceManager_SourceAdded - aSource: {0}, time to read: {1}", aSource, aSource.TimeToRead ); |
|
228 |
|
229 aSource.EventHandler += new CodeSource.EventHandlerFunction( Source_EventHandler ); |
|
230 bool needToSave = true; |
|
231 |
|
232 // If we're in async mode and the source wants to be read immediately |
|
233 // then kick it off. If we're operating in sync mode, then this will |
|
234 // be done as part of the loop within 'Prime'. |
|
235 if ( iSynchronicity == TSynchronicity.EAsynchronous ) |
|
236 { |
|
237 if ( aSource.TimeToRead == CodeSource.TTimeToRead.EReadWhenPriming ) |
|
238 { |
|
239 aSource.Read( iSynchronicity ); |
|
240 } |
|
241 else |
|
242 { |
|
243 // This source will read it's data on it's own terms so skip |
|
244 // it to ensure that we can track when all the other sources |
|
245 // (that do actually read their files now..) are ready. |
|
246 Skip( aSource ); |
|
247 } |
|
248 |
|
249 // We don't need to add it to the 'yet to be primed' list because |
|
250 // it's either been 'read' or then it only supports read-on-demand. |
|
251 needToSave = false; |
|
252 } |
|
253 |
|
254 if ( needToSave ) |
|
255 { |
|
256 lock ( iSourcesYetToBePrimed ) |
|
257 { |
|
258 iSourcesYetToBePrimed.Add( aSource ); |
|
259 } |
|
260 } |
|
261 |
|
262 base.Engine.Trace( string.Format( "[SourceManager_SourceAdded] {0}, srcCount: {1}, yetToBePrimed: {2}", aSource.URI, SourceManager.Count, iSourcesYetToBePrimed.Count ) ); |
|
263 } |
|
264 |
|
265 private void SourceManager_SourceRemoved( CodeSource aSource ) |
|
266 { |
|
267 base.Engine.Trace( string.Format( "[SourceManager_SourceRemoved] START - {0}, srcCount: {1}, yetToBePrimed: {2}", aSource.URI, SourceManager.Count, iSourcesYetToBePrimed.Count ) ); |
|
268 |
|
269 aSource.EventHandler -= new CodeSource.EventHandlerFunction( Source_EventHandler ); |
|
270 |
|
271 base.RemoveFromCompleted( aSource ); |
|
272 lock ( iSourcesYetToBePrimed ) |
|
273 { |
|
274 iSourcesYetToBePrimed.Remove( aSource ); |
|
275 } |
|
276 |
|
277 // Check for completion since removing a source might mean that we've now |
|
278 // reached completed state (if it was the last one that we were waiting for) |
|
279 bool amComplete = base.IsComplete; |
|
280 if ( amComplete ) |
|
281 { |
|
282 CheckForCompletion( amComplete ); |
|
283 } |
|
284 |
|
285 base.Engine.Trace( string.Format( "[SourceManager_SourceRemoved] END - {0}, srcCount: {1}, yetToBePrimed: {2}, amComplete: {3}", aSource.URI, SourceManager.Count, iSourcesYetToBePrimed.Count, amComplete ) ); |
|
286 } |
|
287 |
|
288 private void Source_EventHandler( CodeSource.TEvent aEvent, CodeSource aSource, object aData ) |
|
289 { |
|
290 bool primeCompleted = false; |
|
291 |
|
292 // Map source event onto a primer event |
|
293 if ( aEvent == CodeSource.TEvent.EReadingProgress ) |
|
294 { |
|
295 base.SaveLatestProgress( aSource, (int) aData ); |
|
296 } |
|
297 |
|
298 // If all sources are complete, then are we also done? |
|
299 if ( aEvent == CodeSource.TEvent.EReadingComplete ) |
|
300 { |
|
301 // We don't need to listen to this source anymore |
|
302 aSource.EventHandler -= new CodeSource.EventHandlerFunction( Source_EventHandler ); |
|
303 |
|
304 // Source is 100% complete now. |
|
305 base.SaveLatestProgress( aSource, 100 ); |
|
306 |
|
307 // It's complete, so record as such so that we can tell when all the sources |
|
308 // are now ready. |
|
309 primeCompleted = base.AddToCompleted( aSource ); |
|
310 } |
|
311 |
|
312 CheckForCompletion( primeCompleted ); |
|
313 } |
|
314 #endregion |
|
315 |
|
316 #region Internal methods |
|
317 private void Skip( CodeSource aSource ) |
|
318 { |
|
319 System.Diagnostics.Debug.Assert( aSource.TimeToRead == CodeSource.TTimeToRead.EReadWhenNeeded ); |
|
320 bool primeCompleted = base.AddToCompleted( aSource ); |
|
321 CheckForCompletion( primeCompleted ); |
|
322 } |
|
323 |
|
324 private void SourceEventsUnsubscribe() |
|
325 { |
|
326 foreach ( CodeSource source in SourceManager ) |
|
327 { |
|
328 source.EventHandler -= new CodeSource.EventHandlerFunction( Source_EventHandler ); |
|
329 } |
|
330 } |
|
331 |
|
332 private void CheckForCompletion( bool aAmIComplete ) |
|
333 { |
|
334 // Report any progress |
|
335 base.ReportProgressIfNeeded( aAmIComplete ); |
|
336 |
|
337 // Tidy up and report completion |
|
338 if ( aAmIComplete ) |
|
339 { |
|
340 OnPrimeComplete(); |
|
341 } |
|
342 } |
|
343 |
|
344 private void RecordAlreadyCompletedSources() |
|
345 { |
|
346 CodeSourceManager sourceManager = this.SourceManager; |
|
347 foreach ( CodeSource source in sourceManager ) |
|
348 { |
|
349 base.AddToCompleted( source ); |
|
350 } |
|
351 } |
|
352 #endregion |
|
353 |
|
354 #region Data members |
|
355 private TSynchronicity iSynchronicity = TSynchronicity.ESynchronous; |
|
356 private CodeSourceCollection iSources = new CodeSourceCollection(); |
|
357 private CodeSourceCollection iSourcesYetToBePrimed = new CodeSourceCollection(); |
|
358 #endregion |
|
359 } |
|
360 } |