1 /* 2 * Copyright (C) 2008 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 /** 18 * One line from the loaded-classes file. 19 */ 20 class Record { 21 22 enum Type { 23 /** Start of initialization. */ 24 START_LOAD, 25 26 /** End of initialization. */ 27 END_LOAD, 28 29 /** Start of initialization. */ 30 START_INIT, 31 32 /** End of initialization. */ 33 END_INIT 34 } 35 36 /** Parent process ID. */ 37 final int ppid; 38 39 /** Process ID. */ 40 final int pid; 41 42 /** Thread ID. */ 43 final int tid; 44 45 /** Process name. */ 46 final String processName; 47 48 /** Class loader pointer. */ 49 final int classLoader; 50 51 /** Type of record. */ 52 final Type type; 53 54 /** Name of loaded class. */ 55 final String className; 56 57 /** Record time (ns). */ 58 final long time; 59 60 /** Source file line# */ 61 int sourceLineNumber; 62 63 /** 64 * Parses a line from the loaded-classes file. 65 */ Record(String line, int lineNum)66 Record(String line, int lineNum) { 67 char typeChar = line.charAt(0); 68 switch (typeChar) { 69 case '>': type = Type.START_LOAD; break; 70 case '<': type = Type.END_LOAD; break; 71 case '+': type = Type.START_INIT; break; 72 case '-': type = Type.END_INIT; break; 73 default: throw new AssertionError("Bad line: " + line); 74 } 75 76 sourceLineNumber = lineNum; 77 78 line = line.substring(1); 79 String[] parts = line.split(":"); 80 81 ppid = Integer.parseInt(parts[0]); 82 pid = Integer.parseInt(parts[1]); 83 tid = Integer.parseInt(parts[2]); 84 85 processName = decode(parts[3]).intern(); 86 87 classLoader = Integer.parseInt(parts[4]); 88 className = vmTypeToLanguage(decode(parts[5])).intern(); 89 90 time = Long.parseLong(parts[6]); 91 } 92 93 /** 94 * Decode any escaping that may have been written to the log line. 95 * 96 * Supports unicode-style escaping: \\uXXXX = character in hex 97 * 98 * @param rawField the field as it was written into the log 99 * @result the same field with any escaped characters replaced 100 */ decode(String rawField)101 String decode(String rawField) { 102 String result = rawField; 103 int offset = result.indexOf("\\u"); 104 while (offset >= 0) { 105 String before = result.substring(0, offset); 106 String escaped = result.substring(offset+2, offset+6); 107 String after = result.substring(offset+6); 108 109 result = String.format("%s%c%s", before, Integer.parseInt(escaped, 16), after); 110 111 // find another but don't recurse 112 offset = result.indexOf("\\u", offset + 1); 113 } 114 return result; 115 } 116 117 /** 118 * Converts a VM-style name to a language-style name. 119 */ vmTypeToLanguage(String typeName)120 String vmTypeToLanguage(String typeName) { 121 // if the typename is (null), just return it as-is. This is probably in dexopt and 122 // will be discarded anyway. NOTE: This corresponds to the case in dalvik/vm/oo/Class.c 123 // where dvmLinkClass() returns false and we clean up and exit. 124 if ("(null)".equals(typeName)) { 125 return typeName; 126 } 127 128 if (!typeName.startsWith("L") || !typeName.endsWith(";") ) { 129 throw new AssertionError("Bad name: " + typeName + " in line " + sourceLineNumber); 130 } 131 132 typeName = typeName.substring(1, typeName.length() - 1); 133 return typeName.replace("/", "."); 134 } 135 } 136