25 import com.nokia.s60tools.analyzetool.engine.statistic.BaseInfo; |
25 import com.nokia.s60tools.analyzetool.engine.statistic.BaseInfo; |
26 import com.nokia.s60tools.analyzetool.engine.statistic.DllLoad; |
26 import com.nokia.s60tools.analyzetool.engine.statistic.DllLoad; |
27 import com.nokia.s60tools.analyzetool.engine.statistic.FreeInfo; |
27 import com.nokia.s60tools.analyzetool.engine.statistic.FreeInfo; |
28 import com.nokia.s60tools.analyzetool.engine.statistic.ProcessInfo; |
28 import com.nokia.s60tools.analyzetool.engine.statistic.ProcessInfo; |
29 import com.nokia.s60tools.analyzetool.global.Constants; |
29 import com.nokia.s60tools.analyzetool.global.Constants; |
|
30 import com.nokia.s60tools.analyzetool.global.Util; |
30 import com.nokia.s60tools.analyzetool.global.Constants.Operation; |
31 import com.nokia.s60tools.analyzetool.global.Constants.Operation; |
31 |
32 |
32 /** |
33 /** |
33 * This class parses line of .dat file format and extracts |
34 * This class parses line of .dat file format and extracts callstack data. For |
34 * callstack data. For now this is a separate class but could |
35 * now this is a separate class but could eventually be moved into |
35 * eventually be moved into ParseAnalyzeData after some refactoring. |
36 * ParseAnalyzeData after some re-factoring. |
36 * |
|
37 */ |
37 */ |
38 public class CallstackDataParser { |
38 public class CallstackDataParser { |
39 /** true if callstack reading for this BaseInfo is now complete*/ |
39 |
|
40 /** True, if callstack reading for this BaseInfo is now complete. */ |
40 boolean complete = false; |
41 boolean complete = false; |
41 /** resulting callstack */ |
42 |
|
43 /** Resulting callstack. */ |
42 private List<AllocCallstack> callstack; |
44 private List<AllocCallstack> callstack; |
|
45 |
43 /** BaseInfo for the current alloc or free */ |
46 /** BaseInfo for the current alloc or free */ |
44 private BaseInfo baseInfo; |
47 private BaseInfo baseInfo; |
|
48 |
45 /** ProcessInfo for the current alloc or free */ |
49 /** ProcessInfo for the current alloc or free */ |
46 private ProcessInfo process; |
50 private ProcessInfo process; |
47 /** number of remaining callstack elements expected to be processed with ALLOCF or FREEF */ |
51 |
|
52 /** |
|
53 * Number of remaining callstack elements expected to be processed with ALF |
|
54 * or FRF. |
|
55 */ |
48 private int remainingSize; |
56 private int remainingSize; |
49 |
57 |
50 /** Cache for callstack items. Used when allocation fragment is parsed in the wrong order. */ |
58 /** |
|
59 * Cache for callstack items. Used when allocation fragment is parsed in the |
|
60 * wrong order. |
|
61 */ |
51 private HashMap<Integer, List<AllocCallstack>> callstackCache = null; |
62 private HashMap<Integer, List<AllocCallstack>> callstackCache = null; |
52 |
63 |
53 /** |
64 /** |
54 * Constructor |
65 * Constructor. |
55 * @param baseInfo BaseInfo for the current alloc or free |
66 * |
56 * @param p ProcessInfo for the current alloc or free |
67 * @param baseInfo |
57 */ |
68 * BaseInfo for the current alloc or free |
58 public CallstackDataParser(BaseInfo baseInfo, ProcessInfo p) { |
69 * @param processInfo |
59 if (p == null || baseInfo == null || p.getProcessID() != baseInfo.getProcessID()){ |
70 * ProcessInfo for the current alloc or free |
60 throw new IllegalArgumentException("BaseInfo and ProcessInfo are mandatory and must not be null; and the process id of both must match."); //$NON-NLS-1$ |
71 */ |
|
72 public CallstackDataParser(BaseInfo baseInfo, ProcessInfo processInfo) { |
|
73 if (processInfo == null || baseInfo == null |
|
74 || processInfo.getProcessID() != baseInfo.getProcessID()) { |
|
75 throw new IllegalArgumentException( |
|
76 "BaseInfo and ProcessInfo are mandatory and must not be null; and the process ID of both must match."); //$NON-NLS-1$ |
61 } |
77 } |
62 this.baseInfo = baseInfo; |
78 this.baseInfo = baseInfo; |
63 this.process = p; |
79 this.process = processInfo; |
64 callstack = new ArrayList<AllocCallstack>(); |
80 callstack = new ArrayList<AllocCallstack>(); |
65 } |
81 } |
66 |
82 |
67 /** |
83 /** |
68 * Parses one line of PCSS statement |
84 * Parses one line of <AT> statement. |
|
85 * |
69 * @param aLine |
86 * @param aLine |
70 * @return true, if this callstack is now complete (all parts are available) |
87 * @return true, if this callstack is now complete (all parts are available) |
71 */ |
88 */ |
72 public final boolean parseLine(final String aLine) { |
89 public final boolean parseLine(final String aLine) { |
73 |
90 |
74 if (aLine.indexOf(Constants.PREFIX) == -1){ |
91 if (aLine.indexOf(Constants.PREFIX) == -1) { |
75 //not a PCSS statement |
92 // not a <AT> statement |
76 return false; |
93 return false; |
77 } |
94 } |
78 |
95 |
79 String[] lineFragments = getLineFragments(aLine); |
96 // String[] lineFragments = getLineFragments(aLine); |
80 if (lineFragments.length < 5){ |
97 |
81 //not a valid PCSS statment for callstack processing |
98 int index = aLine.indexOf(Constants.PREFIX); |
|
99 |
|
100 String time = aLine.substring(0, index).trim(); |
|
101 |
|
102 // // get logging time of line in microseconds |
|
103 // long logTime = Util.getLogTime(time); |
|
104 |
|
105 long logTime = Long.parseLong(time, 16); |
|
106 |
|
107 String usedString = aLine.substring(index, aLine.length()); |
|
108 |
|
109 String[] lineFragments = usedString.split(" "); |
|
110 |
|
111 if (lineFragments.length < 5) { |
|
112 // not a valid <AT> statement for callstack processing |
82 return false; |
113 return false; |
83 } |
114 } |
84 int processID = Integer.parseInt(lineFragments[1], 16); |
115 int processID = Integer.parseInt(lineFragments[1], 16); |
85 if (baseInfo.getProcessID() != processID || process.getProcessID() != processID){ |
116 if (baseInfo.getProcessID() != processID |
86 //statement is not for current process id |
117 || process.getProcessID() != processID) { |
87 return false; |
118 // statement is not for current process id |
88 } |
119 return false; |
89 |
120 } |
90 //the operation must match |
121 |
91 Constants.Operation op = Constants.Operation.toOperation(lineFragments[2]); |
122 // the operation must match |
92 if (!verifyOperation(op, baseInfo)){ |
123 Constants.Operation op = Constants.Operation |
93 return false; |
124 .toOperation(lineFragments[2]); |
94 } |
125 if (!verifyOperation(op, baseInfo)) { |
95 |
126 return false; |
96 //the memory address must match |
127 } |
97 long memoryAddress = Long.parseLong(lineFragments[3],16); |
128 |
98 if (baseInfo.getMemoryAddress() != memoryAddress){ |
129 // the memory address must match |
99 return false; |
130 long memoryAddress = Long.parseLong(lineFragments[3], 16); |
100 } |
131 if (baseInfo.getMemoryAddress() != memoryAddress) { |
101 |
132 return false; |
102 |
133 } |
|
134 |
103 boolean ret = false; |
135 boolean ret = false; |
104 |
136 |
105 switch (op) { |
137 switch (op) { |
106 case ALLOC: |
138 case ALH: |
107 ret = parseAlloc(lineFragments); |
|
108 break; |
|
109 case ALLOCH: |
|
110 ret = parseHeader(lineFragments, 6); |
139 ret = parseHeader(lineFragments, 6); |
111 break; |
140 break; |
112 case FREEH: |
141 case FRH: |
113 int traceFileVersion = process.getTraceDataVersion(); |
142 ret = parseHeader(lineFragments, 5); |
114 ret = parseHeader(lineFragments, traceFileVersion > 1 ? 5 : 4); |
|
115 break; |
143 break; |
116 case ALLOCF: |
144 case ALF: |
117 //fall through |
145 // fall through |
118 case FREEF: |
146 case FRF: |
119 ret = parseFragment(lineFragments); |
147 ret = parseFragment(lineFragments, logTime); |
120 break; |
148 break; |
121 default: |
149 default: |
122 // ignore this line |
150 // ignore this line |
123 break; |
151 break; |
124 } |
152 } |
125 |
|
126 return ret; |
153 return ret; |
127 } |
154 } |
128 |
155 |
129 private static boolean verifyOperation(Operation op, BaseInfo aBaseInfo) { |
156 private static boolean verifyOperation(Operation op, BaseInfo aBaseInfo) { |
130 return ((aBaseInfo instanceof AllocInfo && (op == Constants.Operation.ALLOC |
157 return ((aBaseInfo instanceof AllocInfo && (op == Constants.Operation.ALH || op == Constants.Operation.ALF)) || (aBaseInfo instanceof FreeInfo && (op == Constants.Operation.FRH || op == Constants.Operation.FRF))); |
131 || op == Constants.Operation.ALLOCH || op == Constants.Operation.ALLOCF)) |
158 } |
132 || (aBaseInfo instanceof FreeInfo && (op == Constants.Operation.FREEH |
159 |
133 || op == Constants.Operation.FREEF))); |
160 // /** |
134 } |
161 // * Returns the line fragments of the AT statement |
135 |
162 // * |
136 /** |
163 // * @param aLine |
137 * Returns the line fragments of the PCSS statement |
164 // * the AT statement |
138 * @param aLine the PCSS statement |
165 // * @return the line fragments separated by space |
139 * @return the line fragments separated by space |
166 // */ |
140 */ |
167 // private String[] getLineFragments(final String aLine) { |
141 private String[] getLineFragments(final String aLine){ |
168 // int index = aLine.indexOf(Constants.PREFIX); // lines should be preceded |
142 int index = aLine.indexOf(Constants.PREFIX); // lines should be preceded |
169 // |
143 |
170 // if (index == -1) { |
144 if (index == -1){ |
171 // return new String[0]; |
145 return new String[0]; |
172 // } |
146 } |
173 // |
147 |
174 // String usedString = (index == 0) ? aLine : aLine.substring(index, aLine |
148 String usedString = (index == 0) ? aLine : aLine.substring(index, aLine.length()); |
175 // .length()); |
149 return usedString.split(" "); //$NON-NLS-1$ |
176 // return usedString.split(" "); //$NON-NLS-1$ |
150 } |
177 // } |
151 |
178 |
152 /** |
179 // /** |
153 * Parses an ALLOC statement. This statement contains a complete callstack. |
180 // * Parses an ALC statement. This statement contains a complete callstack. |
154 * @param fragments the line fragments of the statement to process |
181 // * |
155 * @return true if statement is complete (this method will always return true) |
182 // * @param fragments |
156 */ |
183 // * the line fragments of the statement to process |
157 private boolean parseAlloc(String[] fragments) { |
184 // * @return true if statement is complete (this method will always return |
158 |
185 // * true) |
159 if (fragments.length > 5) { |
186 // */ |
160 createCallstack(fragments, process, callstack, 6); |
187 // private boolean parseAlloc(String[] fragments) { |
161 } |
188 // |
162 |
189 // if (fragments.length > 5) { |
163 complete = true; |
190 // createCallstack(fragments, process, callstack, 6); |
164 return true; //we are done; there are no fragments for this alloc |
191 // } |
165 } |
192 // |
166 |
193 // complete = true; |
167 /** |
194 // return true; // we are done; there are no fragments for this alloc |
168 * Parses an ALLOCH or FREEH statement |
195 // } |
169 * @param fragments the line fragments of the statement to process |
196 |
170 * @param startIndex index at which the callstack size is to be found |
197 /** |
171 * @return true if callstack is complete, false if callstack fragment is expected |
198 * Parses an ALH or FRH statement |
|
199 * |
|
200 * @param fragments |
|
201 * the line fragments of the statement to process |
|
202 * @param startIndex |
|
203 * index at which the callstack size is to be found |
|
204 * @return true if callstack is complete, false if callstack fragment is |
|
205 * expected |
172 */ |
206 */ |
173 private boolean parseHeader(String[] fragments, int startIndex) { |
207 private boolean parseHeader(String[] fragments, int startIndex) { |
174 |
208 |
175 if (callstack.size() > 0 || remainingSize > 0) { |
209 if (callstack.size() > 0 || remainingSize > 0) { |
176 throw new IllegalStateException( |
210 throw new IllegalStateException( |
177 "Callstack list should still be empty when starting to process ALLOCH"); //$NON-NLS-1$ |
211 "Callstack list should still be empty when starting to process ALH or FRH"); //$NON-NLS-1$ |
178 } |
212 } |
179 |
213 |
180 if (fragments.length > startIndex) { |
214 if (fragments.length > startIndex) { |
181 |
215 |
182 int callstackSize = Integer.parseInt(fragments[startIndex], 16); |
216 int callstackSize = Integer.parseInt(fragments[startIndex], 16); |
183 startIndex ++; |
217 startIndex++; |
184 |
218 |
185 createCallstack(fragments, process, callstack, startIndex); |
219 createCallstack(fragments, process, callstack, startIndex); |
186 |
220 |
187 callstackSize -= callstack.size(); |
221 callstackSize -= callstack.size(); |
188 if (callstackSize > 0) { |
222 if (callstackSize > 0) { |
190 remainingSize = callstackSize; |
224 remainingSize = callstackSize; |
191 } else { |
225 } else { |
192 complete = true; |
226 complete = true; |
193 } |
227 } |
194 } else { |
228 } else { |
195 complete = true; // this header doesn't have callstacks - a bit strange |
229 complete = true; // this header doesn't have callstacks - a bit |
|
230 // strange |
196 } |
231 } |
197 return complete; |
232 return complete; |
198 } |
233 } |
199 |
234 |
200 /** |
235 /** |
201 * Parses an ALLOCF or FREEF callstack fragment |
236 * Parses an ALF or FRF callstack fragment |
202 * @param fragments the line fragments of the statement to process |
237 * |
|
238 * @param fragments |
|
239 * the line fragments of the statement to process |
203 * @return true if callstack is now complete; false otherwise |
240 * @return true if callstack is now complete; false otherwise |
204 */ |
241 */ |
205 private boolean parseFragment(String[] fragments) { |
242 private boolean parseFragment(String[] fragments, long logTime) { |
206 long time = Long.parseLong(fragments[4],16); |
243 long time = process.getStartTime() + (logTime - process.getLogTime()); |
207 int packetNumber = Integer.parseInt(fragments[5], 16); |
244 int packetNumber = Integer.parseInt(fragments[4], 16); |
208 |
245 |
209 if (baseInfo.getTime() == time) { |
246 if (baseInfo.getTime() == time) { |
210 List<AllocCallstack> tmpCallstack = new ArrayList<AllocCallstack>(); |
247 List<AllocCallstack> tmpCallstack = new ArrayList<AllocCallstack>(); |
211 createCallstack(fragments, process, tmpCallstack, 6); |
248 createCallstack(fragments, process, tmpCallstack, 5); |
212 updateFragment(tmpCallstack, packetNumber); |
249 updateFragment(tmpCallstack, packetNumber); |
213 |
250 |
214 remainingSize -= tmpCallstack.size(); |
251 remainingSize -= tmpCallstack.size(); |
215 if (remainingSize <= 0){ |
252 if (remainingSize <= 0) { |
216 complete = true; |
253 complete = true; |
217 finaliseCallstack(); |
254 finaliseCallstack(); |
218 } |
255 } |
219 } |
256 } |
220 return complete; |
257 return complete; |
221 } |
258 } |
222 |
259 |
223 private DllLoad getDllForAddress(ProcessInfo p, Long memoryAddress, |
260 private DllLoad getDllForAddress(ProcessInfo p, Long memoryAddress, |
224 long time) { |
261 long time) { |
225 for (DllLoad oneLoad : p.getDllLoads()) { |
262 for (DllLoad oneLoad : p.getDllLoads()) { |
226 if (memoryAddress >= oneLoad.getStartAddress() |
263 if (memoryAddress >= oneLoad.getStartAddress() |
227 && memoryAddress <= oneLoad.getEndAddress() |
264 && memoryAddress <= oneLoad.getEndAddress() |
231 return oneLoad; |
268 return oneLoad; |
232 } |
269 } |
233 } |
270 } |
234 return null; |
271 return null; |
235 } |
272 } |
236 private void createCallstack(String[] fragments, ProcessInfo p, List<AllocCallstack> callstack, int startIndex) { |
273 |
|
274 private void createCallstack(String[] fragments, ProcessInfo p, |
|
275 List<AllocCallstack> callstack, int startIndex) { |
237 for (int i = startIndex; i < fragments.length; i++) { |
276 for (int i = startIndex; i < fragments.length; i++) { |
238 AllocCallstack callstackElem = new AllocCallstack(fragments[i]); |
277 AllocCallstack callstackElem = new AllocCallstack(fragments[i]); |
239 |
278 |
240 // find matching dll |
279 // find matching dll |
241 DllLoad dllLoad = getDllForAddress(process, callstackElem.getMemoryAddress(), baseInfo.getTime()); |
280 DllLoad dllLoad = getDllForAddress(process, callstackElem |
|
281 .getMemoryAddress(), baseInfo.getTime()); |
242 if (dllLoad != null) { |
282 if (dllLoad != null) { |
243 callstackElem.setDllLoad(dllLoad); |
283 callstackElem.setDllLoad(dllLoad); |
244 } |
284 } |
245 callstack.add(callstackElem); |
285 callstack.add(callstackElem); |
246 } |
286 } |
247 } |
287 } |
|
288 |
248 /** |
289 /** |
249 * Updates allocation fragment. Means that given callstack is addition to |
290 * Updates allocation fragment. Means that given callstack is addition to |
250 * previous added alloc |
291 * previous added alloc |
251 * |
292 * |
252 * @param callstack |
293 * @param callstack |
253 * Addition tmpcallstack items |
294 * Addition tmpcallstack items |
254 * @param packetNumber |
295 * @param packetNumber |
255 * ordinal of callstack fragment |
296 * ordinal of callstack fragment |
256 */ |
297 */ |
257 private void updateFragment(List<AllocCallstack> tmpcallstack, |
298 private void updateFragment(List<AllocCallstack> tmpcallstack, |
258 int packetNumber) { |
299 int packetNumber) { |
259 if (packetNumber == 1){ |
300 if (packetNumber == 1) { |
260 //special case; this can be added to the end of the list straight away |
301 // special case; this can be added to the end of the list straight |
|
302 // away |
261 callstack.addAll(tmpcallstack); |
303 callstack.addAll(tmpcallstack); |
262 } else { |
304 } else { |
263 //packages may come out of order; this is managed in the callstackCache |
305 // packages may come out of order; this is managed in the |
264 if (callstackCache == null){ |
306 // callstackCache |
265 callstackCache = new HashMap<Integer, List<AllocCallstack>>(); |
307 if (callstackCache == null) { |
266 } |
308 callstackCache = new HashMap<Integer, List<AllocCallstack>>(); |
267 callstackCache.put(packetNumber, tmpcallstack); |
309 } |
268 } |
310 callstackCache.put(packetNumber, tmpcallstack); |
269 } |
311 } |
270 /** |
312 } |
271 * Optimises internal callstack data structures. |
313 |
272 * Should only be called after all data for this memory operation |
314 /** |
273 * has been loaded (i.e. all fragments) |
315 * Optimises internal callstack data structures. Should only be called after |
274 */ |
316 * all data for this memory operation has been loaded (i.e. all fragments) |
275 public void finaliseCallstack(){ |
317 */ |
276 if (callstackCache == null){ |
318 public void finaliseCallstack() { |
277 //nothing to do |
319 if (callstackCache == null) { |
|
320 // nothing to do |
278 return; |
321 return; |
279 } |
322 } |
280 |
323 |
281 if (!complete){ |
324 if (!complete) { |
282 throw new IllegalStateException("callstack processing is not yet complete."); //$NON-NLS-1$ |
325 throw new IllegalStateException( |
283 } |
326 "callstack processing is not yet complete."); //$NON-NLS-1$ |
284 |
327 } |
285 if (callstack == null && callstackCache != null){ |
328 |
286 throw new IllegalStateException(); //first set of callstacks should always be in callstacks |
329 if (callstack == null && callstackCache != null) { |
287 } |
330 throw new IllegalStateException(); // first set of callstacks should |
288 |
331 // always be in callstacks |
289 if (callstackCache != null){ |
332 } |
|
333 |
|
334 if (callstackCache != null) { |
290 int size = callstackCache.size(); |
335 int size = callstackCache.size(); |
291 int i = 2; |
336 int i = 2; |
292 while(size != 0){ |
337 while (size != 0) { |
293 List<AllocCallstack> nextCallStacks = callstackCache.get(i); |
338 List<AllocCallstack> nextCallStacks = callstackCache.get(i); |
294 if (nextCallStacks != null){ |
339 if (nextCallStacks != null) { |
295 size --; |
340 size--; |
296 callstack.addAll(nextCallStacks); |
341 callstack.addAll(nextCallStacks); |
297 } //TODO else: missing callstack: shall we report it or log it? |
342 } // TODO else: missing callstack: shall we report it or log it? |
298 i++; |
343 i++; |
299 } |
344 } |
300 callstackCache = null; |
345 callstackCache = null; |
301 } |
346 } |
302 } |
347 } |
303 |
348 |
304 /** |
349 /** |
305 * @return the completed callstack. |
350 * @return the completed callstack. |
306 */ |
351 */ |
307 public List<AllocCallstack> getCallstack(){ |
352 public List<AllocCallstack> getCallstack() { |
308 if (!complete){ |
353 if (!complete) { |
309 throw new IllegalStateException("Callstack has not been completely processed."); //$NON-NLS-1$ |
354 throw new IllegalStateException( |
310 } |
355 "Callstack has not been completely processed."); //$NON-NLS-1$ |
311 |
356 } |
|
357 |
312 finaliseCallstack(); |
358 finaliseCallstack(); |
313 return callstack; |
359 return callstack; |
314 } |
360 } |
315 |
361 |
316 /** |
362 /** |
317 * Forces the callstack state to be set to complete. This should only be used when the end of file is encountered. |
363 * Forces the callstack state to be set to complete. This should only be |
318 */ |
364 * used when the end of file is encountered. |
319 public void forceComplete(){ |
365 */ |
|
366 public void forceComplete() { |
320 complete = true; |
367 complete = true; |
321 finaliseCallstack(); |
368 finaliseCallstack(); |
322 } |
369 } |
323 |
|
324 } |
370 } |