Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 // Copyright 2014 The Go Authors. All rights reserved. | 1 // Copyright 2014 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package server | 5 package server |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "fmt" | 9 "fmt" |
10 "math" | 10 "math" |
11 | 11 |
12 "code.google.com/p/ogle/arch" | 12 "code.google.com/p/ogle/arch" |
13 "code.google.com/p/ogle/debug/dwarf" | 13 "code.google.com/p/ogle/debug/dwarf" |
14 ) | 14 ) |
15 | 15 |
16 // Routines to print a value using DWARF type descriptions. | 16 // Routines to print a value using DWARF type descriptions. |
nigeltao
2014/07/15 04:14:41
s/Routines/Methods that don't take an implicit 'th
| |
17 // TODO: Does this deserve its own package? It has no dependencies on Server. | 17 // TODO: Does this deserve its own package? It has no dependencies on Server. |
18 | 18 |
19 // A Printer pretty-prints a values in the target address space. | 19 // A Printer pretty-prints a values in the target address space. |
20 // It can be reused after each printing operation to avoid unnecessary | 20 // It can be reused after each printing operation to avoid unnecessary |
21 // allocations. However, it is not safe for concurrent access. | 21 // allocations. However, it is not safe for concurrent access. |
22 type Printer struct { | 22 type Printer struct { |
23 err error // Sticky error value. | 23 err error // Sticky error value. |
24 peeker Peeker | 24 peeker Peeker |
25 dwarf *dwarf.Data | 25 dwarf *dwarf.Data |
26 arch *arch.Architecture | 26 arch *arch.Architecture |
(...skipping 12 matching lines...) Expand all Loading... | |
39 | 39 |
40 // errorf sets the sticky error for the printer, if not already set. | 40 // errorf sets the sticky error for the printer, if not already set. |
41 func (p *Printer) errorf(format string, args ...interface{}) { | 41 func (p *Printer) errorf(format string, args ...interface{}) { |
42 if p.err != nil { | 42 if p.err != nil { |
43 return | 43 return |
44 } | 44 } |
45 p.err = fmt.Errorf(format, args...) | 45 p.err = fmt.Errorf(format, args...) |
46 } | 46 } |
47 | 47 |
48 // ok checks the error. If it is the first non-nil error encountered, | 48 // ok checks the error. If it is the first non-nil error encountered, |
49 // it is printed to printBuf, parenthesized for discimination, and remembered. | 49 // it is printed to printBuf, parenthesized for discrimination, and remembered. |
nigeltao
2014/07/15 04:14:40
Typo in "discrimination".
r
2014/07/15 04:56:51
Done.
| |
50 func (p *Printer) ok(err error) bool { | 50 func (p *Printer) ok(err error) bool { |
51 » if p.err != nil && err != nil { | 51 » if p.err == nil && err != nil { |
nigeltao
2014/07/15 04:14:40
The first != should be ==.
r
2014/07/15 04:56:51
Done.
| |
52 p.printf("(%s)", err) | 52 p.printf("(%s)", err) |
53 p.err = err | 53 p.err = err |
54 } | 54 } |
55 return p.err == nil | 55 return p.err == nil |
56 } | 56 } |
57 | 57 |
58 // peek reads len bytes at offset, leaving p.tmp with the data and sized appropr iately. | 58 // peek reads len bytes at offset, leaving p.tmp with the data and sized appropr iately. |
59 func (p *Printer) peek(offset uintptr, len int64) bool { | 59 func (p *Printer) peek(offset uintptr, len int64) bool { |
60 p.tmp = p.tmp[:len] | 60 p.tmp = p.tmp[:len] |
nigeltao
2014/07/15 04:14:40
What happens if you try to peek at a large array?
r
2014/07/15 04:56:50
it's never large. the only big thing we read this
| |
61 return p.ok(p.peeker.peek(offset, p.tmp)) | 61 return p.ok(p.peeker.peek(offset, p.tmp)) |
62 } | 62 } |
63 | 63 |
64 // Peeker is like a read that probes the remote address space. | 64 // Peeker is like a read that probes the remote address space. |
65 type Peeker interface { | 65 type Peeker interface { |
66 peek(offset uintptr, buf []byte) error | 66 peek(offset uintptr, buf []byte) error |
67 } | 67 } |
68 | 68 |
69 // NewPrinter returns a printer that can use the Peeker to access and print | 69 // NewPrinter returns a printer that can use the Peeker to access and print |
70 // values of the specified architecture described by the provided DWARF data. | 70 // values of the specified architecture described by the provided DWARF data. |
71 func NewPrinter(arch *arch.Architecture, dwarf *dwarf.Data, peeker Peeker) *Prin ter { | 71 func NewPrinter(arch *arch.Architecture, dwarf *dwarf.Data, peeker Peeker) *Prin ter { |
72 return &Printer{ | 72 return &Printer{ |
73 peeker: peeker, | 73 peeker: peeker, |
74 arch: arch, | 74 arch: arch, |
75 dwarf: dwarf, | 75 dwarf: dwarf, |
76 visited: make(map[uintptr]bool), | 76 visited: make(map[uintptr]bool), |
77 tmp: make([]byte, 100), // Enough for a largish string. | 77 tmp: make([]byte, 100), // Enough for a largish string. |
nigeltao
2014/07/15 04:14:40
You could delete this line if you sized tmp on dem
r
2014/07/15 04:56:51
see above
| |
78 } | 78 } |
79 } | 79 } |
80 | 80 |
81 // reset resets the Printer. It must be called before starting a new | 81 // reset resets the Printer. It must be called before starting a new |
82 // printing operation. | 82 // printing operation. |
83 func (p *Printer) reset() { | 83 func (p *Printer) reset() { |
84 p.err = nil | 84 p.err = nil |
85 p.printBuf.Reset() | 85 p.printBuf.Reset() |
86 // Just wipe the map rather than reallocating. It's almost always tiny. | 86 // Just wipe the map rather than reallocating. It's almost always tiny. |
87 for k := range p.visited { | 87 for k := range p.visited { |
(...skipping 26 matching lines...) Expand all Loading... | |
114 const ( | 114 const ( |
115 locationAddr = 0x03 | 115 locationAddr = 0x03 |
116 ) | 116 ) |
117 | 117 |
118 // decodeLocation decodes the dwarf data describing an address. | 118 // decodeLocation decodes the dwarf data describing an address. |
119 func (p *Printer) decodeLocation(data []byte) uintptr { | 119 func (p *Printer) decodeLocation(data []byte) uintptr { |
120 switch data[0] { | 120 switch data[0] { |
121 case locationAddr: | 121 case locationAddr: |
122 return uintptr(p.arch.Uintptr(data[1:])) | 122 return uintptr(p.arch.Uintptr(data[1:])) |
123 default: | 123 default: |
124 » » p.errorf("(unimplemented location type %#x)", data[0]) | 124 » » p.errorf("unimplemented location type %#x", data[0]) |
nigeltao
2014/07/15 04:14:40
Drop the inner ()s?
r
2014/07/15 04:56:51
Done.
| |
125 } | 125 } |
126 return 0 | 126 return 0 |
127 } | 127 } |
128 | 128 |
129 // SprintEntry returns the pretty-printed value of the item with the specified D WARF Entry and address. | 129 // SprintEntry returns the pretty-printed value of the item with the specified D WARF Entry and address. |
130 func (p *Printer) SprintEntry(entry *dwarf.Entry, addr uintptr) (string, error) { | 130 func (p *Printer) SprintEntry(entry *dwarf.Entry, addr uintptr) (string, error) { |
131 p.reset() | 131 p.reset() |
132 p.printEntryValueAt(entry, addr) | 132 p.printEntryValueAt(entry, addr) |
133 return p.printBuf.String(), p.err | 133 return p.printBuf.String(), p.err |
134 } | 134 } |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
192 p.printf("%d", p.arch.UintN(p.tmp)) | 192 p.printf("%d", p.arch.UintN(p.tmp)) |
193 } | 193 } |
194 case *dwarf.FloatType: | 194 case *dwarf.FloatType: |
195 if !p.peek(addr, typ.ByteSize) { | 195 if !p.peek(addr, typ.ByteSize) { |
196 return | 196 return |
197 } | 197 } |
198 switch typ.ByteSize { | 198 switch typ.ByteSize { |
199 case 4: | 199 case 4: |
200 p.printf("%g", math.Float32frombits(uint32(p.arch.UintN( p.tmp)))) | 200 p.printf("%g", math.Float32frombits(uint32(p.arch.UintN( p.tmp)))) |
201 case 8: | 201 case 8: |
202 p.printf("%g", math.Float64frombits(p.arch.UintN(p.tmp)) ) | 202 p.printf("%g", math.Float64frombits(p.arch.UintN(p.tmp)) ) |
nigeltao
2014/07/15 04:14:40
I don't know my hardware architectures that well..
r
2014/07/15 04:56:51
we'll find out, but i believe this is correct as w
| |
203 default: | 203 default: |
204 p.errorf("unrecognized float size %d", typ.ByteSize) | 204 p.errorf("unrecognized float size %d", typ.ByteSize) |
205 } | 205 } |
206 case *dwarf.ComplexType: | 206 case *dwarf.ComplexType: |
207 if !p.peek(addr, typ.ByteSize) { | 207 if !p.peek(addr, typ.ByteSize) { |
208 return | 208 return |
209 } | 209 } |
210 switch typ.ByteSize { | 210 switch typ.ByteSize { |
211 case 8: | 211 case 8: |
212 r := math.Float32frombits(uint32(p.arch.UintN(p.tmp[:4]) )) | 212 r := math.Float32frombits(uint32(p.arch.UintN(p.tmp[:4]) )) |
213 i := math.Float32frombits(uint32(p.arch.UintN(p.tmp[4:8] ))) | 213 i := math.Float32frombits(uint32(p.arch.UintN(p.tmp[4:8] ))) |
214 p.printf("%g", complex(r, i)) | 214 p.printf("%g", complex(r, i)) |
215 case 16: | 215 case 16: |
216 r := math.Float64frombits(p.arch.UintN(p.tmp[:8])) | 216 r := math.Float64frombits(p.arch.UintN(p.tmp[:8])) |
217 i := math.Float64frombits(p.arch.UintN(p.tmp[8:16])) | 217 i := math.Float64frombits(p.arch.UintN(p.tmp[8:16])) |
218 p.printf("%g", complex(r, i)) | 218 p.printf("%g", complex(r, i)) |
219 default: | 219 default: |
220 p.errorf("unrecognized complex size %d", typ.ByteSize) | 220 p.errorf("unrecognized complex size %d", typ.ByteSize) |
221 } | 221 } |
222 case *dwarf.StructType: | 222 case *dwarf.StructType: |
223 p.visited[addr] = true | 223 p.visited[addr] = true |
224 if typ.Kind != "struct" { | 224 if typ.Kind != "struct" { |
225 // Could be "class" or "union". | 225 // Could be "class" or "union". |
226 p.errorf("can't handle struct type %s", typ.Kind) | 226 p.errorf("can't handle struct type %s", typ.Kind) |
227 return | 227 return |
228 } | 228 } |
229 p.printf("%s {", typ.String()) | 229 p.printf("%s {", typ.String()) |
230 » » first := true | 230 » » for i, field := range typ.Field { |
231 » » for _, field := range typ.Field { | 231 » » » if i != 0 { |
232 » » » if !first { | |
233 p.printf(", ") | 232 p.printf(", ") |
234 } | 233 } |
235 first = false | |
236 p.printValueAt(field.Type, addr+uintptr(field.ByteOffset )) | 234 p.printValueAt(field.Type, addr+uintptr(field.ByteOffset )) |
237 } | 235 } |
238 p.printf("}") | 236 p.printf("}") |
239 case *dwarf.ArrayType: | 237 case *dwarf.ArrayType: |
240 p.printArrayAt(typ, addr) | 238 p.printArrayAt(typ, addr) |
241 case *dwarf.MapType: | 239 case *dwarf.MapType: |
242 p.visited[addr] = true | 240 p.visited[addr] = true |
243 p.printMapAt(typ, addr) | 241 p.printMapAt(typ, addr) |
244 case *dwarf.SliceType: | 242 case *dwarf.SliceType: |
245 p.visited[addr] = true | 243 p.visited[addr] = true |
246 p.printSliceAt(typ, addr) | 244 p.printSliceAt(typ, addr) |
247 case *dwarf.StringType: | 245 case *dwarf.StringType: |
248 p.printStringAt(typ, addr) | 246 p.printStringAt(typ, addr) |
249 case *dwarf.TypedefType: | 247 case *dwarf.TypedefType: |
250 p.errorf("unimplemented typedef type %T %v", typ, typ) | 248 p.errorf("unimplemented typedef type %T %v", typ, typ) |
nigeltao
2014/07/15 04:14:41
Is the "%T" useful? Isn't it just "*dwarf.TypedefT
r
2014/07/15 04:56:50
%T tells us what the dwarf type is. %s shows us wh
| |
251 default: | 249 default: |
252 // TODO: chan func interface | 250 // TODO: chan func interface |
253 p.errorf("unimplemented type %v", typ) | 251 p.errorf("unimplemented type %v", typ) |
254 } | 252 } |
255 } | 253 } |
256 | 254 |
257 func (p *Printer) printArrayAt(typ *dwarf.ArrayType, addr uintptr) { | 255 func (p *Printer) printArrayAt(typ *dwarf.ArrayType, addr uintptr) { |
258 if !p.peek(addr, typ.ByteSize) { | |
259 p.errorf("array has no defined size") | |
nigeltao
2014/07/15 04:14:40
I might be misunderstanding the code, but "no defi
r
2014/07/15 04:56:51
This code is wrong anyway. There's no point in rea
| |
260 return | |
261 } | |
262 elemType := typ.Type | 256 elemType := typ.Type |
263 length := typ.Count | 257 length := typ.Count |
264 stride := typ.StrideBitSize | 258 stride := typ.StrideBitSize |
265 if stride > 0 { | 259 if stride > 0 { |
266 stride /= 8 | 260 stride /= 8 |
267 } else { | 261 } else { |
268 stride = p.sizeof(elemType) | 262 stride = p.sizeof(elemType) |
269 if stride < 0 { | 263 if stride < 0 { |
270 » » » p.errorf("array elements have no defined size") | 264 » » » p.errorf("array elements have no known size") |
271 } | 265 } |
272 } | 266 } |
273 p.printf("%s{", typ) | 267 p.printf("%s{", typ) |
274 » first := true | 268 » n := length |
275 » for i := int64(0); i < length; i++ { | 269 » if n > 100 { |
276 » » if !first { | 270 » » n = 100 // TODO: Have a way to control this? |
nigeltao
2014/07/15 04:14:40
This could just be "if i != 0 {" and you could the
r
2014/07/15 04:56:50
Done.
r
2014/07/15 04:56:51
Done.
| |
271 » } | |
272 » for i := int64(0); i < n; i++ { | |
273 » » if i != 0 { | |
277 p.printf(", ") | 274 p.printf(", ") |
278 } | 275 } |
279 first = false | |
280 p.printValueAt(elemType, addr) | 276 p.printValueAt(elemType, addr) |
281 addr += uintptr(stride) // TODO: Alignment and padding - not giv en by Type | 277 addr += uintptr(stride) // TODO: Alignment and padding - not giv en by Type |
278 } | |
279 if n < length { | |
280 p.printf(", ...") | |
282 } | 281 } |
283 p.printf("}") | 282 p.printf("}") |
284 } | 283 } |
285 | 284 |
286 // TODO: Unimplemented. | 285 // TODO: Unimplemented. |
287 func (p *Printer) printMapAt(typ *dwarf.MapType, addr uintptr) { | 286 func (p *Printer) printMapAt(typ *dwarf.MapType, addr uintptr) { |
288 // Maps are pointers to a struct type. | 287 // Maps are pointers to a struct type. |
289 structType := typ.Type.(*dwarf.PtrType).Type.(*dwarf.StructType) | 288 structType := typ.Type.(*dwarf.PtrType).Type.(*dwarf.StructType) |
290 // Indirect through the pointer. | 289 // Indirect through the pointer. |
291 if !p.peek(addr, int64(p.arch.PointerSize)) { | 290 if !p.peek(addr, int64(p.arch.PointerSize)) { |
292 return | 291 return |
293 } | 292 } |
294 addr = uintptr(p.arch.Uintptr(p.tmp[:p.arch.PointerSize])) | 293 addr = uintptr(p.arch.Uintptr(p.tmp[:p.arch.PointerSize])) |
295 // Now read the struct. | 294 // Now read the struct. |
296 if !p.peek(addr, structType.ByteSize) { | 295 if !p.peek(addr, structType.ByteSize) { |
297 return | 296 return |
298 } | 297 } |
299 // TODO: unimplemented. Hard. | 298 // TODO: unimplemented. Hard. |
300 countField := structType.Field[0] | 299 countField := structType.Field[0] |
301 p.printf("%s with ", typ) | 300 p.printf("%s with ", typ) |
302 p.printValueAt(countField.Type, addr+uintptr(countField.ByteOffset)) | 301 p.printValueAt(countField.Type, addr+uintptr(countField.ByteOffset)) |
303 p.printf(" elements") | 302 p.printf(" elements") |
304 } | 303 } |
305 | 304 |
306 func (p *Printer) printSliceAt(typ *dwarf.SliceType, addr uintptr) { | 305 func (p *Printer) printSliceAt(typ *dwarf.SliceType, addr uintptr) { |
307 // Slices look like a struct with fields array *elemtype, len uint32/64, cap uint32/64. | 306 // Slices look like a struct with fields array *elemtype, len uint32/64, cap uint32/64. |
308 // BUG: Slice header appears to have fields with ByteSize == 0 | 307 // BUG: Slice header appears to have fields with ByteSize == 0 |
309 if !p.peek(addr, typ.ByteSize) { | 308 if !p.peek(addr, typ.ByteSize) { |
310 » » p.errorf("slice has no defined size") | 309 » » p.errorf("slice header has no known size") |
311 return | 310 return |
312 } | 311 } |
313 lo := typ.Field[0].ByteOffset | 312 lo := typ.Field[0].ByteOffset |
314 hi := lo + int64(p.arch.PointerSize) | 313 hi := lo + int64(p.arch.PointerSize) |
315 ptr := uintptr(p.arch.UintN(p.tmp[lo:hi])) | 314 ptr := uintptr(p.arch.UintN(p.tmp[lo:hi])) |
316 lo = typ.Field[1].ByteOffset | 315 lo = typ.Field[1].ByteOffset |
317 hi = lo + int64(p.arch.IntSize) | 316 hi = lo + int64(p.arch.IntSize) |
318 length := p.arch.UintN(p.tmp[lo:hi]) | 317 length := p.arch.UintN(p.tmp[lo:hi]) |
319 // Capacity is unused here. | 318 // Capacity is unused here. |
320 elemType := typ.ElemType | 319 elemType := typ.ElemType |
321 size := p.sizeof(elemType) | 320 size := p.sizeof(elemType) |
322 if size < 0 { | 321 if size < 0 { |
323 return | 322 return |
324 } | 323 } |
325 p.printf("%s{", typ) | 324 p.printf("%s{", typ) |
326 first := true | |
327 for i := uint64(0); i < length; i++ { | 325 for i := uint64(0); i < length; i++ { |
328 » » if !first { | 326 » » if i != 0 { |
329 p.printf(", ") | 327 p.printf(", ") |
330 } | 328 } |
331 first = false | |
332 p.printValueAt(elemType, ptr) | 329 p.printValueAt(elemType, ptr) |
333 ptr += uintptr(size) // TODO: Alignment and padding - not given by Type | 330 ptr += uintptr(size) // TODO: Alignment and padding - not given by Type |
334 } | 331 } |
335 p.printf("}") | 332 p.printf("}") |
336 } | 333 } |
337 | 334 |
338 func (p *Printer) printStringAt(typ *dwarf.StringType, addr uintptr) { | 335 func (p *Printer) printStringAt(typ *dwarf.StringType, addr uintptr) { |
339 // Strings look like a struct with fields array *elemtype, len uint64. | 336 // Strings look like a struct with fields array *elemtype, len uint64. |
340 // TODO uint64 on 386 too? | 337 // TODO uint64 on 386 too? |
341 if !p.peek(addr, typ.ByteSize) { | 338 if !p.peek(addr, typ.ByteSize) { |
342 » » p.errorf("string has no defined size") | 339 » » p.errorf("string header has no known size") |
343 return | 340 return |
344 } | 341 } |
345 // BUG: String header appears to have fields with ByteSize == 0 | 342 // BUG: String header appears to have fields with ByteSize == 0 |
346 lo := typ.Field[0].ByteOffset | 343 lo := typ.Field[0].ByteOffset |
347 hi := lo + int64(p.arch.PointerSize) | 344 hi := lo + int64(p.arch.PointerSize) |
348 ptr := p.arch.UintN(p.tmp[lo:hi]) | 345 ptr := p.arch.UintN(p.tmp[lo:hi]) |
349 lo = typ.Field[1].ByteOffset | 346 lo = typ.Field[1].ByteOffset |
350 hi = lo + int64(p.arch.IntSize) // TODO? | 347 hi = lo + int64(p.arch.IntSize) // TODO? |
351 length := p.arch.UintN(p.tmp[lo:hi]) | 348 length := p.arch.UintN(p.tmp[lo:hi]) |
352 if length > uint64(cap(p.tmp)) { | 349 if length > uint64(cap(p.tmp)) { |
353 if p.peek(uintptr(ptr), int64(cap(p.tmp))) { | 350 if p.peek(uintptr(ptr), int64(cap(p.tmp))) { |
354 p.printf("%q...", p.tmp) | 351 p.printf("%q...", p.tmp) |
355 } | 352 } |
356 } else { | 353 } else { |
357 if p.peek(uintptr(ptr), int64(length)) { | 354 if p.peek(uintptr(ptr), int64(length)) { |
358 p.printf("%q", p.tmp[:length]) | 355 p.printf("%q", p.tmp[:length]) |
359 } | 356 } |
360 } | 357 } |
361 } | 358 } |
362 | 359 |
363 // sizeof returns the byte size of the type. It returns -1 if no size can be fou nd. | 360 // sizeof returns the byte size of the type. It returns -1 if no size can be fou nd. |
364 func (p *Printer) sizeof(typ dwarf.Type) int64 { | 361 func (p *Printer) sizeof(typ dwarf.Type) int64 { |
365 size := typ.Size() // Will be -1 if ByteSize is not set. | 362 size := typ.Size() // Will be -1 if ByteSize is not set. |
366 if size >= 0 { | 363 if size >= 0 { |
367 return size | 364 return size |
368 } | 365 } |
369 switch typ.(type) { | 366 switch typ.(type) { |
370 default: | |
nigeltao
2014/07/15 04:14:41
You could drop the default case:
switch typ.(type
r
2014/07/15 04:56:51
Done.
| |
371 p.errorf("unknown size for %s", typ.String()) | |
nigeltao
2014/07/15 04:14:40
Add
size = -1
after this. Or drop the default case
nigeltao
2014/07/15 04:14:40
The ".String()" is unnecessary.
r
2014/07/15 04:56:51
Done.
r
2014/07/15 04:56:51
Done.
| |
372 case *dwarf.PtrType: | 367 case *dwarf.PtrType: |
373 » » size = int64(p.arch.PointerSize) | 368 » » // This is the only one we know of, but more may arise. |
374 » } | 369 » » return int64(p.arch.PointerSize) |
375 » return size | 370 » } |
376 } | 371 » p.errorf("unknown size for %s", typ) |
372 » return -1 | |
373 } | |
LEFT | RIGHT |