1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.traceview; 18 19 import java.io.BufferedReader; 20 import java.io.File; 21 import java.io.FileInputStream; 22 import java.io.FileNotFoundException; 23 import java.io.IOException; 24 import java.io.InputStreamReader; 25 import java.nio.BufferUnderflowException; 26 import java.nio.ByteOrder; 27 import java.nio.MappedByteBuffer; 28 import java.nio.channels.FileChannel; 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.Collection; 32 import java.util.Comparator; 33 import java.util.HashMap; 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 36 37 public class DmTraceReader extends TraceReader { 38 private static final int TRACE_MAGIC = 0x574f4c53; 39 40 private static final int METHOD_TRACE_ENTER = 0x00; // method entry 41 private static final int METHOD_TRACE_EXIT = 0x01; // method exit 42 private static final int METHOD_TRACE_UNROLL = 0x02; // method exited by exception unrolling 43 44 // When in dual clock mode, we report that a context switch has occurred 45 // when skew between the real time and thread cpu clocks is more than this 46 // many microseconds. 47 private static final long MIN_CONTEXT_SWITCH_TIME_USEC = 100; 48 49 private enum ClockSource { 50 THREAD_CPU, WALL, DUAL, 51 }; 52 53 private int mVersionNumber; 54 private boolean mRegression; 55 private ProfileProvider mProfileProvider; 56 private String mTraceFileName; 57 private MethodData mTopLevel; 58 private ArrayList<Call> mCallList; 59 private HashMap<String, String> mPropertiesMap; 60 private HashMap<Integer, MethodData> mMethodMap; 61 private HashMap<Integer, ThreadData> mThreadMap; 62 private ThreadData[] mSortedThreads; 63 private MethodData[] mSortedMethods; 64 private long mTotalCpuTime; 65 private long mTotalRealTime; 66 private MethodData mContextSwitch; 67 private int mRecordSize; 68 private ClockSource mClockSource; 69 70 // A regex for matching the thread "id name" lines in the .key file 71 private static final Pattern mIdNamePattern = Pattern.compile("(\\d+)\t(.*)"); //$NON-NLS-1$ 72 DmTraceReader(String traceFileName, boolean regression)73 public DmTraceReader(String traceFileName, boolean regression) throws IOException { 74 mTraceFileName = traceFileName; 75 mRegression = regression; 76 mPropertiesMap = new HashMap<String, String>(); 77 mMethodMap = new HashMap<Integer, MethodData>(); 78 mThreadMap = new HashMap<Integer, ThreadData>(); 79 mCallList = new ArrayList<Call>(); 80 81 // Create a single top-level MethodData object to hold the profile data 82 // for time spent in the unknown caller. 83 mTopLevel = new MethodData(0, "(toplevel)"); 84 mContextSwitch = new MethodData(-1, "(context switch)"); 85 mMethodMap.put(0, mTopLevel); 86 mMethodMap.put(-1, mContextSwitch); 87 generateTrees(); 88 } 89 generateTrees()90 void generateTrees() throws IOException { 91 long offset = parseKeys(); 92 parseData(offset); 93 analyzeData(); 94 } 95 96 @Override getProfileProvider()97 public ProfileProvider getProfileProvider() { 98 if (mProfileProvider == null) 99 mProfileProvider = new ProfileProvider(this); 100 return mProfileProvider; 101 } 102 mapFile(String filename, long offset)103 private MappedByteBuffer mapFile(String filename, long offset) throws IOException { 104 MappedByteBuffer buffer = null; 105 FileInputStream dataFile = new FileInputStream(filename); 106 File file = new File(filename); 107 FileChannel fc = dataFile.getChannel(); 108 buffer = fc.map(FileChannel.MapMode.READ_ONLY, offset, file.length() - offset); 109 buffer.order(ByteOrder.LITTLE_ENDIAN); 110 111 return buffer; 112 } 113 readDataFileHeader(MappedByteBuffer buffer)114 private void readDataFileHeader(MappedByteBuffer buffer) { 115 int magic = buffer.getInt(); 116 if (magic != TRACE_MAGIC) { 117 System.err.printf( 118 "Error: magic number mismatch; got 0x%x, expected 0x%x\n", 119 magic, TRACE_MAGIC); 120 throw new RuntimeException(); 121 } 122 123 // read version 124 int version = buffer.getShort(); 125 if (version != mVersionNumber) { 126 System.err.printf( 127 "Error: version number mismatch; got %d in data header but %d in options\n", 128 version, mVersionNumber); 129 throw new RuntimeException(); 130 } 131 if (version < 1 || version > 3) { 132 System.err.printf( 133 "Error: unsupported trace version number %d. " 134 + "Please use a newer version of TraceView to read this file.", version); 135 throw new RuntimeException(); 136 } 137 138 // read offset 139 int offsetToData = buffer.getShort() - 16; 140 141 // read startWhen 142 buffer.getLong(); 143 144 // read record size 145 if (version == 1) { 146 mRecordSize = 9; 147 } else if (version == 2) { 148 mRecordSize = 10; 149 } else { 150 mRecordSize = buffer.getShort(); 151 offsetToData -= 2; 152 } 153 154 // Skip over offsetToData bytes 155 while (offsetToData-- > 0) { 156 buffer.get(); 157 } 158 } 159 parseData(long offset)160 private void parseData(long offset) throws IOException { 161 MappedByteBuffer buffer = mapFile(mTraceFileName, offset); 162 readDataFileHeader(buffer); 163 164 ArrayList<TraceAction> trace = null; 165 if (mClockSource == ClockSource.THREAD_CPU) { 166 trace = new ArrayList<TraceAction>(); 167 } 168 169 final boolean haveThreadClock = mClockSource != ClockSource.WALL; 170 final boolean haveGlobalClock = mClockSource != ClockSource.THREAD_CPU; 171 172 // Parse all call records to obtain elapsed time information. 173 ThreadData prevThreadData = null; 174 for (;;) { 175 int threadId; 176 int methodId; 177 long threadTime, globalTime; 178 try { 179 int recordSize = mRecordSize; 180 181 if (mVersionNumber == 1) { 182 threadId = buffer.get(); 183 recordSize -= 1; 184 } else { 185 threadId = buffer.getShort(); 186 recordSize -= 2; 187 } 188 189 methodId = buffer.getInt(); 190 recordSize -= 4; 191 192 switch (mClockSource) { 193 case WALL: 194 threadTime = 0; 195 globalTime = buffer.getInt(); 196 recordSize -= 4; 197 break; 198 case DUAL: 199 threadTime = buffer.getInt(); 200 globalTime = buffer.getInt(); 201 recordSize -= 8; 202 break; 203 default: 204 case THREAD_CPU: 205 threadTime = buffer.getInt(); 206 globalTime = 0; 207 recordSize -= 4; 208 break; 209 } 210 211 while (recordSize-- > 0) { 212 buffer.get(); 213 } 214 } catch (BufferUnderflowException ex) { 215 break; 216 } 217 218 int methodAction = methodId & 0x03; 219 methodId = methodId & ~0x03; 220 MethodData methodData = mMethodMap.get(methodId); 221 if (methodData == null) { 222 String name = String.format("(0x%1$x)", methodId); //$NON-NLS-1$ 223 methodData = new MethodData(methodId, name); 224 mMethodMap.put(methodId, methodData); 225 } 226 227 ThreadData threadData = mThreadMap.get(threadId); 228 if (threadData == null) { 229 String name = String.format("[%1$d]", threadId); //$NON-NLS-1$ 230 threadData = new ThreadData(threadId, name, mTopLevel); 231 mThreadMap.put(threadId, threadData); 232 } 233 234 long elapsedGlobalTime = 0; 235 if (haveGlobalClock) { 236 if (!threadData.mHaveGlobalTime) { 237 threadData.mGlobalStartTime = globalTime; 238 threadData.mHaveGlobalTime = true; 239 } else { 240 elapsedGlobalTime = globalTime - threadData.mGlobalEndTime; 241 } 242 threadData.mGlobalEndTime = globalTime; 243 } 244 245 if (haveThreadClock) { 246 long elapsedThreadTime = 0; 247 if (!threadData.mHaveThreadTime) { 248 threadData.mThreadStartTime = threadTime; 249 threadData.mThreadCurrentTime = threadTime; 250 threadData.mHaveThreadTime = true; 251 } else { 252 elapsedThreadTime = threadTime - threadData.mThreadEndTime; 253 } 254 threadData.mThreadEndTime = threadTime; 255 256 if (!haveGlobalClock) { 257 // Detect context switches whenever execution appears to switch from one 258 // thread to another. This assumption is only valid on uniprocessor 259 // systems (which is why we now have a dual clock mode). 260 // We represent context switches in the trace by pushing a call record 261 // with MethodData mContextSwitch onto the stack of the previous 262 // thread. We arbitrarily set the start and end time of the context 263 // switch such that the context switch occurs in the middle of the thread 264 // time and itself accounts for zero thread time. 265 if (prevThreadData != null && prevThreadData != threadData) { 266 // Begin context switch from previous thread. 267 Call switchCall = prevThreadData.enter(mContextSwitch, trace); 268 switchCall.mThreadStartTime = prevThreadData.mThreadEndTime; 269 mCallList.add(switchCall); 270 271 // Return from context switch to current thread. 272 Call top = threadData.top(); 273 if (top.getMethodData() == mContextSwitch) { 274 threadData.exit(mContextSwitch, trace); 275 long beforeSwitch = elapsedThreadTime / 2; 276 top.mThreadStartTime += beforeSwitch; 277 top.mThreadEndTime = top.mThreadStartTime; 278 } 279 } 280 prevThreadData = threadData; 281 } else { 282 // If we have a global clock, then we can detect context switches (or blocking 283 // calls or cpu suspensions or clock anomalies) by comparing global time to 284 // thread time for successive calls that occur on the same thread. 285 // As above, we represent the context switch using a special method call. 286 long sleepTime = elapsedGlobalTime - elapsedThreadTime; 287 if (sleepTime > MIN_CONTEXT_SWITCH_TIME_USEC) { 288 Call switchCall = threadData.enter(mContextSwitch, trace); 289 long beforeSwitch = elapsedThreadTime / 2; 290 long afterSwitch = elapsedThreadTime - beforeSwitch; 291 switchCall.mGlobalStartTime = globalTime - elapsedGlobalTime + beforeSwitch; 292 switchCall.mGlobalEndTime = globalTime - afterSwitch; 293 switchCall.mThreadStartTime = threadTime - afterSwitch; 294 switchCall.mThreadEndTime = switchCall.mThreadStartTime; 295 threadData.exit(mContextSwitch, trace); 296 mCallList.add(switchCall); 297 } 298 } 299 300 // Add thread CPU time. 301 Call top = threadData.top(); 302 top.addCpuTime(elapsedThreadTime); 303 } 304 305 switch (methodAction) { 306 case METHOD_TRACE_ENTER: { 307 Call call = threadData.enter(methodData, trace); 308 if (haveGlobalClock) { 309 call.mGlobalStartTime = globalTime; 310 } 311 if (haveThreadClock) { 312 call.mThreadStartTime = threadTime; 313 } 314 mCallList.add(call); 315 break; 316 } 317 case METHOD_TRACE_EXIT: 318 case METHOD_TRACE_UNROLL: { 319 Call call = threadData.exit(methodData, trace); 320 if (call != null) { 321 if (haveGlobalClock) { 322 call.mGlobalEndTime = globalTime; 323 } 324 if (haveThreadClock) { 325 call.mThreadEndTime = threadTime; 326 } 327 } 328 break; 329 } 330 default: 331 throw new RuntimeException("Unrecognized method action: " + methodAction); 332 } 333 } 334 335 // Exit any pending open-ended calls. 336 for (ThreadData threadData : mThreadMap.values()) { 337 threadData.endTrace(trace); 338 } 339 340 // Recreate the global timeline from thread times, if needed. 341 if (!haveGlobalClock) { 342 long globalTime = 0; 343 prevThreadData = null; 344 for (TraceAction traceAction : trace) { 345 Call call = traceAction.mCall; 346 ThreadData threadData = call.getThreadData(); 347 348 if (traceAction.mAction == TraceAction.ACTION_ENTER) { 349 long threadTime = call.mThreadStartTime; 350 globalTime += call.mThreadStartTime - threadData.mThreadCurrentTime; 351 call.mGlobalStartTime = globalTime; 352 if (!threadData.mHaveGlobalTime) { 353 threadData.mHaveGlobalTime = true; 354 threadData.mGlobalStartTime = globalTime; 355 } 356 threadData.mThreadCurrentTime = threadTime; 357 } else if (traceAction.mAction == TraceAction.ACTION_EXIT) { 358 long threadTime = call.mThreadEndTime; 359 globalTime += call.mThreadEndTime - threadData.mThreadCurrentTime; 360 call.mGlobalEndTime = globalTime; 361 threadData.mGlobalEndTime = globalTime; 362 threadData.mThreadCurrentTime = threadTime; 363 } // else, ignore ACTION_INCOMPLETE calls, nothing to do 364 prevThreadData = threadData; 365 } 366 } 367 368 // Finish updating all calls and calculate the total time spent. 369 for (int i = mCallList.size() - 1; i >= 0; i--) { 370 Call call = mCallList.get(i); 371 372 // Calculate exclusive real-time by subtracting inclusive real time 373 // accumulated by children from the total span. 374 long realTime = call.mGlobalEndTime - call.mGlobalStartTime; 375 call.mExclusiveRealTime = Math.max(realTime - call.mInclusiveRealTime, 0); 376 call.mInclusiveRealTime = realTime; 377 378 call.finish(); 379 } 380 mTotalCpuTime = 0; 381 mTotalRealTime = 0; 382 for (ThreadData threadData : mThreadMap.values()) { 383 Call rootCall = threadData.getRootCall(); 384 threadData.updateRootCallTimeBounds(); 385 rootCall.finish(); 386 mTotalCpuTime += rootCall.mInclusiveCpuTime; 387 mTotalRealTime += rootCall.mInclusiveRealTime; 388 } 389 390 if (mRegression) { 391 System.out.format("totalCpuTime %dus\n", mTotalCpuTime); 392 System.out.format("totalRealTime %dus\n", mTotalRealTime); 393 394 dumpThreadTimes(); 395 dumpCallTimes(); 396 } 397 } 398 399 static final int PARSE_VERSION = 0; 400 static final int PARSE_THREADS = 1; 401 static final int PARSE_METHODS = 2; 402 static final int PARSE_OPTIONS = 4; 403 parseKeys()404 long parseKeys() throws IOException { 405 BufferedReader in = null; 406 try { 407 in = new BufferedReader(new InputStreamReader( 408 new FileInputStream(mTraceFileName), "US-ASCII")); 409 } catch (FileNotFoundException ex) { 410 System.err.println(ex.getMessage()); 411 } 412 413 long offset = 0; 414 int mode = PARSE_VERSION; 415 String line = null; 416 while (true) { 417 line = in.readLine(); 418 if (line == null) { 419 throw new IOException("Key section does not have an *end marker"); 420 } 421 422 // Calculate how much we have read from the file so far. The 423 // extra byte is for the line ending not included by readLine(). 424 offset += line.length() + 1; 425 if (line.startsWith("*")) { 426 if (line.equals("*version")) { 427 mode = PARSE_VERSION; 428 continue; 429 } 430 if (line.equals("*threads")) { 431 mode = PARSE_THREADS; 432 continue; 433 } 434 if (line.equals("*methods")) { 435 mode = PARSE_METHODS; 436 continue; 437 } 438 if (line.equals("*end")) { 439 break; 440 } 441 } 442 switch (mode) { 443 case PARSE_VERSION: 444 mVersionNumber = Integer.decode(line); 445 mode = PARSE_OPTIONS; 446 break; 447 case PARSE_THREADS: 448 parseThread(line); 449 break; 450 case PARSE_METHODS: 451 parseMethod(line); 452 break; 453 case PARSE_OPTIONS: 454 parseOption(line); 455 break; 456 } 457 } 458 459 if (mClockSource == null) { 460 mClockSource = ClockSource.THREAD_CPU; 461 } 462 return offset; 463 } 464 parseOption(String line)465 void parseOption(String line) { 466 String[] tokens = line.split("="); 467 if (tokens.length == 2) { 468 String key = tokens[0]; 469 String value = tokens[1]; 470 mPropertiesMap.put(key, value); 471 472 if (key.equals("clock")) { 473 if (value.equals("thread-cpu")) { 474 mClockSource = ClockSource.THREAD_CPU; 475 } else if (value.equals("wall")) { 476 mClockSource = ClockSource.WALL; 477 } else if (value.equals("dual")) { 478 mClockSource = ClockSource.DUAL; 479 } 480 } 481 } 482 } 483 parseThread(String line)484 void parseThread(String line) { 485 String idStr = null; 486 String name = null; 487 Matcher matcher = mIdNamePattern.matcher(line); 488 if (matcher.find()) { 489 idStr = matcher.group(1); 490 name = matcher.group(2); 491 } 492 if (idStr == null) return; 493 if (name == null) name = "(unknown)"; 494 495 int id = Integer.decode(idStr); 496 mThreadMap.put(id, new ThreadData(id, name, mTopLevel)); 497 } 498 parseMethod(String line)499 void parseMethod(String line) { 500 String[] tokens = line.split("\t"); 501 int id = Long.decode(tokens[0]).intValue(); 502 String className = tokens[1]; 503 String methodName = null; 504 String signature = null; 505 String pathname = null; 506 int lineNumber = -1; 507 if (tokens.length == 6) { 508 methodName = tokens[2]; 509 signature = tokens[3]; 510 pathname = tokens[4]; 511 lineNumber = Integer.decode(tokens[5]); 512 pathname = constructPathname(className, pathname); 513 } else if (tokens.length > 2) { 514 if (tokens[3].startsWith("(")) { 515 methodName = tokens[2]; 516 signature = tokens[3]; 517 } else { 518 pathname = tokens[2]; 519 lineNumber = Integer.decode(tokens[3]); 520 } 521 } 522 523 mMethodMap.put(id, new MethodData(id, className, methodName, signature, 524 pathname, lineNumber)); 525 } 526 constructPathname(String className, String pathname)527 private String constructPathname(String className, String pathname) { 528 int index = className.lastIndexOf('/'); 529 if (index > 0 && index < className.length() - 1 530 && pathname.endsWith(".java")) 531 pathname = className.substring(0, index + 1) + pathname; 532 return pathname; 533 } 534 analyzeData()535 private void analyzeData() { 536 final TimeBase timeBase = getPreferredTimeBase(); 537 538 // Sort the threads into decreasing cpu time 539 Collection<ThreadData> tv = mThreadMap.values(); 540 mSortedThreads = tv.toArray(new ThreadData[tv.size()]); 541 Arrays.sort(mSortedThreads, new Comparator<ThreadData>() { 542 @Override 543 public int compare(ThreadData td1, ThreadData td2) { 544 if (timeBase.getTime(td2) > timeBase.getTime(td1)) 545 return 1; 546 if (timeBase.getTime(td2) < timeBase.getTime(td1)) 547 return -1; 548 return td2.getName().compareTo(td1.getName()); 549 } 550 }); 551 552 // Sort the methods into decreasing inclusive time 553 Collection<MethodData> mv = mMethodMap.values(); 554 MethodData[] methods; 555 methods = mv.toArray(new MethodData[mv.size()]); 556 Arrays.sort(methods, new Comparator<MethodData>() { 557 @Override 558 public int compare(MethodData md1, MethodData md2) { 559 if (timeBase.getElapsedInclusiveTime(md2) > timeBase.getElapsedInclusiveTime(md1)) 560 return 1; 561 if (timeBase.getElapsedInclusiveTime(md2) < timeBase.getElapsedInclusiveTime(md1)) 562 return -1; 563 return md1.getName().compareTo(md2.getName()); 564 } 565 }); 566 567 // Count the number of methods with non-zero inclusive time 568 int nonZero = 0; 569 for (MethodData md : methods) { 570 if (timeBase.getElapsedInclusiveTime(md) == 0) 571 break; 572 nonZero += 1; 573 } 574 575 // Copy the methods with non-zero time 576 mSortedMethods = new MethodData[nonZero]; 577 int ii = 0; 578 for (MethodData md : methods) { 579 if (timeBase.getElapsedInclusiveTime(md) == 0) 580 break; 581 md.setRank(ii); 582 mSortedMethods[ii++] = md; 583 } 584 585 // Let each method analyze its profile data 586 for (MethodData md : mSortedMethods) { 587 md.analyzeData(timeBase); 588 } 589 590 // Update all the calls to include the method rank in 591 // their name. 592 for (Call call : mCallList) { 593 call.updateName(); 594 } 595 596 if (mRegression) { 597 dumpMethodStats(); 598 } 599 } 600 601 /* 602 * This method computes a list of records that describe the the execution 603 * timeline for each thread. Each record is a pair: (row, block) where: row: 604 * is the ThreadData object block: is the call (containing the start and end 605 * times) 606 */ 607 @Override getThreadTimeRecords()608 public ArrayList<TimeLineView.Record> getThreadTimeRecords() { 609 TimeLineView.Record record; 610 ArrayList<TimeLineView.Record> timeRecs; 611 timeRecs = new ArrayList<TimeLineView.Record>(); 612 613 // For each thread, push a "toplevel" call that encompasses the 614 // entire execution of the thread. 615 for (ThreadData threadData : mSortedThreads) { 616 if (!threadData.isEmpty() && threadData.getId() != 0) { 617 record = new TimeLineView.Record(threadData, threadData.getRootCall()); 618 timeRecs.add(record); 619 } 620 } 621 622 for (Call call : mCallList) { 623 record = new TimeLineView.Record(call.getThreadData(), call); 624 timeRecs.add(record); 625 } 626 627 if (mRegression) { 628 dumpTimeRecs(timeRecs); 629 System.exit(0); 630 } 631 return timeRecs; 632 } 633 dumpThreadTimes()634 private void dumpThreadTimes() { 635 System.out.print("\nThread Times\n"); 636 System.out.print("id t-start t-end g-start g-end name\n"); 637 for (ThreadData threadData : mThreadMap.values()) { 638 System.out.format("%2d %8d %8d %8d %8d %s\n", 639 threadData.getId(), 640 threadData.mThreadStartTime, threadData.mThreadEndTime, 641 threadData.mGlobalStartTime, threadData.mGlobalEndTime, 642 threadData.getName()); 643 } 644 } 645 dumpCallTimes()646 private void dumpCallTimes() { 647 System.out.print("\nCall Times\n"); 648 System.out.print("id t-start t-end g-start g-end excl. incl. method\n"); 649 for (Call call : mCallList) { 650 System.out.format("%2d %8d %8d %8d %8d %8d %8d %s\n", 651 call.getThreadId(), call.mThreadStartTime, call.mThreadEndTime, 652 call.mGlobalStartTime, call.mGlobalEndTime, 653 call.mExclusiveCpuTime, call.mInclusiveCpuTime, 654 call.getMethodData().getName()); 655 } 656 } 657 dumpMethodStats()658 private void dumpMethodStats() { 659 System.out.print("\nMethod Stats\n"); 660 System.out.print("Excl Cpu Incl Cpu Excl Real Incl Real Calls Method\n"); 661 for (MethodData md : mSortedMethods) { 662 System.out.format("%9d %9d %9s %s\n", 663 md.getElapsedExclusiveCpuTime(), md.getElapsedInclusiveCpuTime(), 664 md.getElapsedExclusiveRealTime(), md.getElapsedInclusiveRealTime(), 665 md.getCalls(), md.getProfileName()); 666 } 667 } 668 dumpTimeRecs(ArrayList<TimeLineView.Record> timeRecs)669 private void dumpTimeRecs(ArrayList<TimeLineView.Record> timeRecs) { 670 System.out.print("\nTime Records\n"); 671 System.out.print("id t-start t-end g-start g-end method\n"); 672 for (TimeLineView.Record record : timeRecs) { 673 Call call = (Call) record.block; 674 System.out.format("%2d %8d %8d %8d %8d %s\n", 675 call.getThreadId(), call.mThreadStartTime, call.mThreadEndTime, 676 call.mGlobalStartTime, call.mGlobalEndTime, 677 call.getMethodData().getName()); 678 } 679 } 680 681 @Override getThreadLabels()682 public HashMap<Integer, String> getThreadLabels() { 683 HashMap<Integer, String> labels = new HashMap<Integer, String>(); 684 for (ThreadData t : mThreadMap.values()) { 685 labels.put(t.getId(), t.getName()); 686 } 687 return labels; 688 } 689 690 @Override getMethods()691 public MethodData[] getMethods() { 692 return mSortedMethods; 693 } 694 695 @Override getThreads()696 public ThreadData[] getThreads() { 697 return mSortedThreads; 698 } 699 700 @Override getTotalCpuTime()701 public long getTotalCpuTime() { 702 return mTotalCpuTime; 703 } 704 705 @Override getTotalRealTime()706 public long getTotalRealTime() { 707 return mTotalRealTime; 708 } 709 710 @Override haveCpuTime()711 public boolean haveCpuTime() { 712 return mClockSource != ClockSource.WALL; 713 } 714 715 @Override haveRealTime()716 public boolean haveRealTime() { 717 return mClockSource != ClockSource.THREAD_CPU; 718 } 719 720 @Override getProperties()721 public HashMap<String, String> getProperties() { 722 return mPropertiesMap; 723 } 724 725 @Override getPreferredTimeBase()726 public TimeBase getPreferredTimeBase() { 727 if (mClockSource == ClockSource.WALL) { 728 return TimeBase.REAL_TIME; 729 } 730 return TimeBase.CPU_TIME; 731 } 732 733 @Override getClockSource()734 public String getClockSource() { 735 switch (mClockSource) { 736 case THREAD_CPU: 737 return "cpu time"; 738 case WALL: 739 return "real time"; 740 case DUAL: 741 return "real time, dual clock"; 742 } 743 return null; 744 } 745 } 746