summaryrefslogtreecommitdiff
path: root/src/pkg/exp/ogle/rtype.go
blob: fd77f1bc2483f3f2c659bdac741058de7f70e562 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ogle

import (
	"debug/proc"
	"exp/eval"
	"fmt"
	"log"
)

const debugParseRemoteType = false

// A remoteType is the local representation of a type in a remote process.
type remoteType struct {
	eval.Type
	// The size of values of this type in bytes.
	size int
	// The field alignment of this type.  Only used for
	// manually-constructed types.
	fieldAlign int
	// The maker function to turn a remote address of a value of
	// this type into an interpreter Value.
	mk maker
}

var manualTypes = make(map[Arch]map[eval.Type]*remoteType)

// newManualType constructs a remote type from an interpreter Type
// using the size and alignment properties of the given architecture.
// Most types are parsed directly out of the remote process, but to do
// so we need to layout the structures that describe those types ourselves.
func newManualType(t eval.Type, arch Arch) *remoteType {
	if nt, ok := t.(*eval.NamedType); ok {
		t = nt.Def
	}

	// Get the type map for this architecture
	typeMap := manualTypes[arch]
	if typeMap == nil {
		typeMap = make(map[eval.Type]*remoteType)
		manualTypes[arch] = typeMap

		// Construct basic types for this architecture
		basicType := func(t eval.Type, mk maker, size int, fieldAlign int) {
			t = t.(*eval.NamedType).Def
			if fieldAlign == 0 {
				fieldAlign = size
			}
			typeMap[t] = &remoteType{t, size, fieldAlign, mk}
		}
		basicType(eval.Uint8Type, mkUint8, 1, 0)
		basicType(eval.Uint32Type, mkUint32, 4, 0)
		basicType(eval.UintptrType, mkUintptr, arch.PtrSize(), 0)
		basicType(eval.Int16Type, mkInt16, 2, 0)
		basicType(eval.Int32Type, mkInt32, 4, 0)
		basicType(eval.IntType, mkInt, arch.IntSize(), 0)
		basicType(eval.StringType, mkString, arch.PtrSize()+arch.IntSize(), arch.PtrSize())
	}

	if rt, ok := typeMap[t]; ok {
		return rt
	}

	var rt *remoteType
	switch t := t.(type) {
	case *eval.PtrType:
		var elem *remoteType
		mk := func(r remote) eval.Value { return remotePtr{r, elem} }
		rt = &remoteType{t, arch.PtrSize(), arch.PtrSize(), mk}
		// Construct the element type after registering the
		// type to break cycles.
		typeMap[eval.Type(t)] = rt
		elem = newManualType(t.Elem, arch)

	case *eval.ArrayType:
		elem := newManualType(t.Elem, arch)
		mk := func(r remote) eval.Value { return remoteArray{r, t.Len, elem} }
		rt = &remoteType{t, elem.size * int(t.Len), elem.fieldAlign, mk}

	case *eval.SliceType:
		elem := newManualType(t.Elem, arch)
		mk := func(r remote) eval.Value { return remoteSlice{r, elem} }
		rt = &remoteType{t, arch.PtrSize() + 2*arch.IntSize(), arch.PtrSize(), mk}

	case *eval.StructType:
		layout := make([]remoteStructField, len(t.Elems))
		offset := 0
		fieldAlign := 0
		for i, f := range t.Elems {
			elem := newManualType(f.Type, arch)
			if fieldAlign == 0 {
				fieldAlign = elem.fieldAlign
			}
			offset = arch.Align(offset, elem.fieldAlign)
			layout[i].offset = offset
			layout[i].fieldType = elem
			offset += elem.size
		}
		mk := func(r remote) eval.Value { return remoteStruct{r, layout} }
		rt = &remoteType{t, offset, fieldAlign, mk}

	default:
		log.Panicf("cannot manually construct type %T", t)
	}

	typeMap[t] = rt
	return rt
}

var prtIndent = ""

// parseRemoteType parses a Type structure in a remote process to
// construct the corresponding interpreter type and remote type.
func parseRemoteType(a aborter, rs remoteStruct) *remoteType {
	addr := rs.addr().base
	p := rs.addr().p

	// We deal with circular types by discovering cycles at
	// NamedTypes.  If a type cycles back to something other than
	// a named type, we're guaranteed that there will be a named
	// type somewhere in that cycle.  Thus, we continue down,
	// re-parsing types until we reach the named type in the
	// cycle.  In order to still create one remoteType per remote
	// type, we insert an empty remoteType in the type map the
	// first time we encounter the type and re-use that structure
	// the second time we encounter it.

	rt, ok := p.types[addr]
	if ok && rt.Type != nil {
		return rt
	} else if !ok {
		rt = &remoteType{}
		p.types[addr] = rt
	}

	if debugParseRemoteType {
		sym := p.syms.SymByAddr(uint64(addr))
		name := "<unknown>"
		if sym != nil {
			name = sym.Name
		}
		log.Printf("%sParsing type at %#x (%s)", prtIndent, addr, name)
		prtIndent += " "
		defer func() { prtIndent = prtIndent[0 : len(prtIndent)-1] }()
	}

	// Get Type header
	itype := proc.Word(rs.field(p.f.Type.Typ).(remoteUint).aGet(a))
	typ := rs.field(p.f.Type.Ptr).(remotePtr).aGet(a).(remoteStruct)

	// Is this a named type?
	var nt *eval.NamedType
	uncommon := typ.field(p.f.CommonType.UncommonType).(remotePtr).aGet(a)
	if uncommon != nil {
		name := uncommon.(remoteStruct).field(p.f.UncommonType.Name).(remotePtr).aGet(a)
		if name != nil {
			// TODO(austin) Declare type in appropriate remote package
			nt = eval.NewNamedType(name.(remoteString).aGet(a))
			rt.Type = nt
		}
	}

	// Create type
	var t eval.Type
	var mk maker
	switch itype {
	case p.runtime.PBoolType:
		t = eval.BoolType
		mk = mkBool
	case p.runtime.PUint8Type:
		t = eval.Uint8Type
		mk = mkUint8
	case p.runtime.PUint16Type:
		t = eval.Uint16Type
		mk = mkUint16
	case p.runtime.PUint32Type:
		t = eval.Uint32Type
		mk = mkUint32
	case p.runtime.PUint64Type:
		t = eval.Uint64Type
		mk = mkUint64
	case p.runtime.PUintType:
		t = eval.UintType
		mk = mkUint
	case p.runtime.PUintptrType:
		t = eval.UintptrType
		mk = mkUintptr
	case p.runtime.PInt8Type:
		t = eval.Int8Type
		mk = mkInt8
	case p.runtime.PInt16Type:
		t = eval.Int16Type
		mk = mkInt16
	case p.runtime.PInt32Type:
		t = eval.Int32Type
		mk = mkInt32
	case p.runtime.PInt64Type:
		t = eval.Int64Type
		mk = mkInt64
	case p.runtime.PIntType:
		t = eval.IntType
		mk = mkInt
	case p.runtime.PFloat32Type:
		t = eval.Float32Type
		mk = mkFloat32
	case p.runtime.PFloat64Type:
		t = eval.Float64Type
		mk = mkFloat64
	case p.runtime.PFloatType:
		t = eval.FloatType
		mk = mkFloat
	case p.runtime.PStringType:
		t = eval.StringType
		mk = mkString

	case p.runtime.PArrayType:
		// Cast to an ArrayType
		typ := p.runtime.ArrayType.mk(typ.addr()).(remoteStruct)
		len := int64(typ.field(p.f.ArrayType.Len).(remoteUint).aGet(a))
		elem := parseRemoteType(a, typ.field(p.f.ArrayType.Elem).(remotePtr).aGet(a).(remoteStruct))
		t = eval.NewArrayType(len, elem.Type)
		mk = func(r remote) eval.Value { return remoteArray{r, len, elem} }

	case p.runtime.PStructType:
		// Cast to a StructType
		typ := p.runtime.StructType.mk(typ.addr()).(remoteStruct)
		fs := typ.field(p.f.StructType.Fields).(remoteSlice).aGet(a)

		fields := make([]eval.StructField, fs.Len)
		layout := make([]remoteStructField, fs.Len)
		for i := range fields {
			f := fs.Base.(remoteArray).elem(int64(i)).(remoteStruct)
			elemrs := f.field(p.f.StructField.Typ).(remotePtr).aGet(a).(remoteStruct)
			elem := parseRemoteType(a, elemrs)
			fields[i].Type = elem.Type
			name := f.field(p.f.StructField.Name).(remotePtr).aGet(a)
			if name == nil {
				fields[i].Anonymous = true
			} else {
				fields[i].Name = name.(remoteString).aGet(a)
			}
			layout[i].offset = int(f.field(p.f.StructField.Offset).(remoteUint).aGet(a))
			layout[i].fieldType = elem
		}

		t = eval.NewStructType(fields)
		mk = func(r remote) eval.Value { return remoteStruct{r, layout} }

	case p.runtime.PPtrType:
		// Cast to a PtrType
		typ := p.runtime.PtrType.mk(typ.addr()).(remoteStruct)
		elem := parseRemoteType(a, typ.field(p.f.PtrType.Elem).(remotePtr).aGet(a).(remoteStruct))
		t = eval.NewPtrType(elem.Type)
		mk = func(r remote) eval.Value { return remotePtr{r, elem} }

	case p.runtime.PSliceType:
		// Cast to a SliceType
		typ := p.runtime.SliceType.mk(typ.addr()).(remoteStruct)
		elem := parseRemoteType(a, typ.field(p.f.SliceType.Elem).(remotePtr).aGet(a).(remoteStruct))
		t = eval.NewSliceType(elem.Type)
		mk = func(r remote) eval.Value { return remoteSlice{r, elem} }

	case p.runtime.PMapType, p.runtime.PChanType, p.runtime.PFuncType, p.runtime.PInterfaceType, p.runtime.PUnsafePointerType, p.runtime.PDotDotDotType:
		// TODO(austin)
		t = eval.UintptrType
		mk = mkUintptr

	default:
		sym := p.syms.SymByAddr(uint64(itype))
		name := "<unknown symbol>"
		if sym != nil {
			name = sym.Name
		}
		err := fmt.Sprintf("runtime type at %#x has unexpected type %#x (%s)", addr, itype, name)
		a.Abort(FormatError(err))
	}

	// Fill in the remote type
	if nt != nil {
		nt.Complete(t)
	} else {
		rt.Type = t
	}
	rt.size = int(typ.field(p.f.CommonType.Size).(remoteUint).aGet(a))
	rt.mk = mk

	return rt
}