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