1 /* MemoryMap.java -- Maps address ranges to their data. 2 Copyright (C) 2007 Free Software Foundation 3 4 This file is part of libgcj. 5 6 This software is copyrighted work licensed under the terms of the 7 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for 8 details. */ 9 10 package gnu.gcj.tools.gc_analyze; 11 12 import java.io.BufferedReader; 13 import java.io.EOFException; 14 import java.io.File; 15 import java.io.IOException; 16 import java.io.RandomAccessFile; 17 import java.nio.ByteBuffer; 18 import java.nio.ByteOrder; 19 import java.nio.channels.FileChannel; 20 import java.util.Comparator; 21 import java.util.HashMap; 22 import java.util.SortedSet; 23 import java.util.TreeSet; 24 25 /** 26 * Reads /proc/self/maps output from dump file. 27 * Creates map of <filename> to Range. 28 * 29 * Returns filename given address. 30 * Returns offset given address. 31 * Returns BytePtr given address. 32 * 33 */ 34 class MemoryMap 35 { 36 static class RangeComparator implements Comparator<Range> 37 { compare(Range r1, Range r2)38 public int compare(Range r1, Range r2) 39 { 40 if (r2.end == 0 && r1.end != 0) 41 return -compare(r2, r1); 42 43 if (r1.begin < r2.begin) 44 return -1; 45 else if (r1.begin >= r2.end) 46 return 1; 47 else 48 return 0; 49 } 50 } 51 52 static class Range 53 { 54 long begin; 55 long end; 56 57 long offset; 58 String filename; Range()59 Range() 60 { 61 } 62 Range(long b, long e, String s, long o)63 Range(long b, long e, String s, long o) 64 { 65 begin = b; 66 end = e; 67 filename = s; 68 offset = o; 69 } 70 } 71 72 /** 73 * Parse the string as an unsigned hexadecimal number. This is 74 * similar to Long.parseInt(s,16), but without the restriction that 75 * values that have the sign bit set not being allowed. 76 * 77 * @param s the number as a String. 78 * @return the number. 79 */ parseHexLong(String s)80 static long parseHexLong(String s) 81 { 82 if (s.length() > 16) 83 throw new NumberFormatException(); 84 long r = 0; 85 for (int i = 0; i < s.length(); i++) 86 { 87 int digit = 0; 88 char c = s.charAt(i); 89 switch (c) 90 { 91 case '0': 92 case '1': 93 case '2': 94 case '3': 95 case '4': 96 case '5': 97 case '6': 98 case '7': 99 case '8': 100 case '9': 101 digit = c - '0'; 102 break; 103 case 'a': 104 case 'b': 105 case 'c': 106 case 'd': 107 case 'e': 108 case 'f': 109 digit = 10 + c - 'a'; 110 break; 111 case 'A': 112 case 'B': 113 case 'C': 114 case 'D': 115 case 'E': 116 case 'F': 117 digit = 10 + c - 'A'; 118 break; 119 default: 120 throw new NumberFormatException(); 121 } 122 r = (r << 4) + digit; 123 } 124 return r; 125 } 126 127 // String filename -> Range 128 TreeSet<Range> map = new TreeSet<Range>(new RangeComparator()); 129 HashMap<String, SymbolTable> symbolTables = 130 new HashMap<String, SymbolTable>(); 131 ByteOrder byteOrder; 132 int wordSize; 133 MemoryMap(BufferedReader reader, String rawFileName)134 public MemoryMap(BufferedReader reader, 135 String rawFileName) throws IOException 136 { 137 FileChannel raw = (new RandomAccessFile(rawFileName, "r")).getChannel(); 138 ByteBuffer buf = ByteBuffer.allocate(8); 139 raw.read(buf); 140 if (buf.hasRemaining()) 141 { 142 raw.close(); 143 throw new EOFException(); 144 } 145 buf.flip(); 146 wordSize = buf.get(); 147 148 if (wordSize == 8 || wordSize == 4) 149 byteOrder = ByteOrder.LITTLE_ENDIAN; 150 else 151 { 152 byteOrder = ByteOrder.BIG_ENDIAN; 153 buf.rewind(); 154 wordSize = buf.getInt(); 155 if (0 == wordSize) 156 wordSize = buf.getInt(); 157 } 158 switch (wordSize) 159 { 160 case 4: 161 case 8: 162 break; 163 default: 164 throw new IOException("Bad .bytes file header"); 165 } 166 buf = ByteBuffer.allocate(3 * wordSize); 167 buf.order(byteOrder); 168 raw.position(0L); 169 170 for(;;) 171 { 172 // Read the block header. 173 buf.clear(); 174 if (-1 == raw.read(buf)) 175 { 176 //EOF 177 raw.close(); 178 break; 179 } 180 if (buf.hasRemaining()) 181 { 182 raw.close(); 183 throw new EOFException(); 184 } 185 buf.flip(); 186 long dummy 187 = (wordSize == 4) ? (buf.getInt() & 0xffffffffL) : buf.getLong(); 188 if (dummy != wordSize) 189 throw new IOException("Bad .bytes file header"); 190 long start 191 = wordSize == 4 ? (buf.getInt() & 0xffffffffL) : buf.getLong(); 192 long length 193 = wordSize == 4 ? (buf.getInt() & 0xffffffffL) : buf.getLong(); 194 if (length < 0L) 195 throw new IOException("Bad .bytes file header"); 196 197 long currentPos = raw.position(); 198 raw.position(currentPos + length); 199 200 Range range = new Range(start, start + length, 201 rawFileName, currentPos); 202 map.add(range); 203 } 204 205 for (;;) 206 { 207 String s = reader.readLine(); 208 if (s == null) 209 break; 210 if (s.indexOf("Begin address map") >= 0) 211 { 212 for (;;) 213 { 214 s = reader.readLine(); 215 if (s.indexOf("End address map") >= 0) 216 { 217 dump(); 218 return; 219 } 220 int endOfAddress = s.indexOf('-'); 221 long address = parseHexLong(s.substring(0, endOfAddress)); 222 int endOfAddress2 = s.indexOf(' ', endOfAddress + 1); 223 long address2 = parseHexLong(s.substring(endOfAddress + 1, 224 endOfAddress2)); 225 int endOfOffset = s.indexOf(' ', endOfAddress2 + 6); 226 long offset; 227 try 228 { 229 offset = parseHexLong(s.substring(endOfAddress2 + 6, 230 endOfOffset)); 231 } 232 catch (Exception e) 233 { 234 offset = 0; 235 } 236 int end = s.indexOf('/'); 237 238 if (end > 0) 239 { 240 String file = s.substring(end); 241 if (file.startsWith("/dev/")) 242 continue; 243 244 Range r = new Range(address, address2, file, offset); 245 if (offset == 0) 246 { 247 // Read the file's symbol table 248 try 249 { 250 File f = ToolPrefix.fileForName(file); 251 if (f != null) 252 { 253 SymbolTable st = new SymbolTable(f.getPath()); 254 if (st.loadAddr != address) 255 st.relocation = address - st.loadAddr; 256 symbolTables.put(file, st); 257 } 258 } 259 catch (Exception ex) 260 { 261 ex.printStackTrace(); 262 } 263 } 264 map.add(r); 265 } 266 } // inner loop 267 } // started inner loop 268 } // outer loop - finding begin 269 } // memoryMap 270 271 dump()272 public void dump() 273 { 274 System.out.println("MemoryMap:"); 275 for (Range r : map) 276 { 277 System.out.println(Long.toHexString(r.begin) + "-" 278 + Long.toHexString(r.end) + " -> " 279 + r.filename + " offset " 280 + Long.toHexString(r.offset)); 281 } 282 } 283 getRange(long addr)284 Range getRange(long addr) 285 { 286 Range r = new Range(); 287 r.begin = addr; 288 SortedSet<Range> t = map.tailSet(r); 289 if (t.isEmpty()) 290 return null; 291 Range c = t.first(); 292 if (c.begin <= addr && addr < c.end) 293 return c; 294 return null; 295 } 296 getFile(long addr)297 String getFile(long addr) 298 { 299 Range r = getRange(addr); 300 if (null != r) 301 return r.filename; 302 return null; 303 } 304 getOffset(long addr)305 long getOffset(long addr) 306 { 307 Range r = getRange(addr); 308 if (null != r) 309 return r.offset; 310 return 0L; 311 } 312 313 /** 314 * @return BytePtr which includes given address. 315 */ getBytePtr(long addr, int length)316 BytePtr getBytePtr(long addr, int length) throws IOException 317 { 318 Range r = getRange(addr); 319 320 if (null == r) 321 return null; 322 323 File f = ToolPrefix.fileForName(r.filename); 324 if (null == f) 325 return null; 326 327 if (addr + length > r.end) 328 length = (int)(r.end - addr); 329 330 ByteBuffer b = ByteBuffer.allocate(length); 331 b.order(byteOrder); 332 333 FileChannel fc = (new RandomAccessFile(f, "r")).getChannel(); 334 fc.position(r.offset + addr - r.begin); 335 int nr = fc.read(b); 336 fc.close(); 337 if (nr != length) 338 return null; 339 b.flip(); 340 return new BytePtr(b, wordSize); 341 } 342 getSymbol(long addr)343 public String getSymbol(long addr) 344 { 345 Range r = getRange(addr); 346 347 if (r == null) 348 return null; 349 350 SymbolTable st = symbolTables.get(r.filename); 351 if (st == null) 352 return null; 353 354 // Apply relocation 355 addr -= st.relocation; 356 357 return st.getSymbol(addr); 358 } 359 } 360