Source file
src/runtime/hexdump.go
1
2
3
4
5 package runtime
6
7 import (
8 "internal/goarch"
9 "unsafe"
10 )
11
12
13
14
15 func hexdumpWords(p, len uintptr, mark func(uintptr, hexdumpMarker)) {
16 printlock()
17
18
19 symMark := func(u uintptr, hm hexdumpMarker) {
20 if mark != nil {
21 mark(u, hm)
22 }
23
24
25 val := *(*uintptr)(unsafe.Pointer(u))
26 fn := findfunc(val)
27 if fn.valid() {
28 hm.start()
29 print("<", funcname(fn), "+", hex(val-fn.entry()), ">\n")
30 }
31 }
32
33 h := hexdumper{addr: p, mark: symMark}
34 h.write(unsafe.Slice((*byte)(unsafe.Pointer(p)), len))
35 h.close()
36 printunlock()
37 }
38
39
40
41
42
43 type hexdumper struct {
44
45 addr uintptr
46
47
48
49 addrBytes uint8
50
51
52
53
54
55 wordBytes uint8
56
57
58
59
60
61
62
63
64 mark func(addr uintptr, m hexdumpMarker)
65
66
67
68 ready int8
69
70
71
72 dataBuf [16]byte
73 dataPos uint8
74 dataSkip uint8
75
76
77 toPos [16]byte
78 }
79
80 type hexdumpMarker struct {
81 chars int
82 }
83
84 func (h *hexdumper) write(data []byte) {
85 if h.ready == 0 {
86 h.init()
87 }
88
89
90 if h.dataPos > 0 {
91 n := copy(h.dataBuf[h.dataPos:], data)
92 h.dataPos += uint8(n)
93 data = data[n:]
94 if h.dataPos < uint8(len(h.dataBuf)) {
95 return
96 }
97 h.flushLine(h.dataBuf[:])
98 h.dataPos = 0
99 }
100
101
102 for len(data) >= len(h.dataBuf) {
103 h.flushLine(data[:len(h.dataBuf)])
104 data = data[len(h.dataBuf):]
105 }
106
107
108 h.dataPos = uint8(copy(h.dataBuf[:], data))
109 }
110
111 func (h *hexdumper) close() {
112 if h.dataPos > 0 {
113 h.flushLine(h.dataBuf[:h.dataPos])
114 }
115 }
116
117 func (h *hexdumper) init() {
118 const bytesPerLine = len(h.dataBuf)
119
120 if h.addrBytes == 0 {
121 h.addrBytes = goarch.PtrSize
122 } else if h.addrBytes < 0 || h.addrBytes > goarch.PtrSize {
123 throw("invalid addrBytes")
124 }
125
126 if h.wordBytes == 0 {
127 h.wordBytes = goarch.PtrSize
128 }
129 wb := int(h.wordBytes)
130 if wb < 0 || wb >= bytesPerLine || wb&(wb-1) != 0 {
131 throw("invalid wordBytes")
132 }
133
134
135 for i := range h.toPos {
136
137 field := 0
138 if goarch.BigEndian {
139 field = i
140 } else {
141 field = i ^ int(wb-1)
142 }
143
144
145 h.toPos[i] = byte(field*2 + field/4 + field/8)
146 }
147
148
149
150 nAddr := h.addr &^ uintptr(bytesPerLine-1)
151
152 h.dataPos = uint8(h.addr - nAddr)
153 h.dataSkip = uint8(h.addr - nAddr)
154 h.addr = nAddr
155
156
157 h.ready = 1
158 }
159
160 func (h *hexdumper) flushLine(data []byte) {
161 const bytesPerLine = len(h.dataBuf)
162
163 const maxAddrChars = 2 * goarch.PtrSize
164 const addrSep = ": "
165 dataStart := int(2*h.addrBytes) + len(addrSep)
166
167
168 const dataChars = (bytesPerLine-1)*2 + (bytesPerLine-1)/4 + (bytesPerLine-1)/8 + 2
169 const asciiSep = " "
170 asciiStart := dataStart + dataChars + len(asciiSep)
171 const asciiChars = bytesPerLine
172 nlPos := asciiStart + asciiChars
173
174 var lineBuf [maxAddrChars + len(addrSep) + dataChars + len(asciiSep) + asciiChars + 1]byte
175 clear := func() {
176 for i := range lineBuf {
177 lineBuf[i] = ' '
178 }
179 }
180 clear()
181
182 if h.ready == 1 {
183
184 for offset, pos := range h.toPos {
185 h.fmtHex(lineBuf[dataStart+int(pos+1):][:1], uint64(offset))
186 }
187
188 for offset := range asciiChars {
189 h.fmtHex(lineBuf[asciiStart+offset:][:1], uint64(offset))
190 }
191 lineBuf[nlPos] = '\n'
192 gwrite(lineBuf[:nlPos+1])
193 clear()
194 h.ready = 2
195 }
196
197
198 h.fmtHex(lineBuf[:2*h.addrBytes], uint64(h.addr))
199 copy(lineBuf[2*h.addrBytes:], addrSep)
200
201 for offset, b := range data {
202 if offset < int(h.dataSkip) {
203 continue
204 }
205
206 pos := h.toPos[offset]
207 h.fmtHex(lineBuf[dataStart+int(pos):][:2], uint64(b))
208
209 copy(lineBuf[dataStart+dataChars:], asciiSep)
210 ascii := uint8('.')
211 if b >= ' ' && b <= '~' {
212 ascii = b
213 }
214 lineBuf[asciiStart+offset] = ascii
215 }
216
217 end := asciiStart + len(data)
218 lineBuf[end] = '\n'
219 buf := lineBuf[:end+1]
220
221
222 gwrite(buf)
223
224
225 if h.mark != nil {
226 clear()
227 for offset := 0; offset+int(h.wordBytes) <= len(data); offset += int(h.wordBytes) {
228 if offset < int(h.dataSkip) {
229 continue
230 }
231 addr := h.addr + uintptr(offset)
232
233 caret := dataStart + int(min(h.toPos[offset], h.toPos[offset+int(h.wordBytes)-1]))
234 h.mark(addr, hexdumpMarker{caret})
235 }
236 }
237
238 h.addr += uintptr(bytesPerLine)
239 h.dataPos = 0
240 h.dataSkip = 0
241 }
242
243
244
245 func (h *hexdumper) fmtHex(buf []byte, v uint64) {
246 const dig = "0123456789abcdef"
247 i := len(buf) - 1
248 for ; i >= 0; i-- {
249 buf[i] = dig[v%16]
250 v /= 16
251 }
252 if v != 0 {
253
254 buf[0] = '*'
255 }
256 }
257
258 func (m hexdumpMarker) start() {
259 var spaces [64]byte
260 for i := range spaces {
261 spaces[i] = ' '
262 }
263 for m.chars > len(spaces) {
264 gwrite(spaces[:])
265 m.chars -= len(spaces)
266 }
267 gwrite(spaces[:m.chars])
268 print("^ ")
269 }
270
View as plain text