Ai
1 Star 0 Fork 0

7x24/google-cloud-go
关闭

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
print.go 14.30 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build linux
package server
import (
"bytes"
"fmt"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/arch"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf"
)
// typeAndAddress associates an address in the target with a DWARF type.
type typeAndAddress struct {
Type dwarf.Type
Address uint64
}
// Routines to print a value using DWARF type descriptions.
// TODO: Does this deserve its own package? It has no dependencies on Server.
// A Printer pretty-prints values in the target address space.
// It can be reused after each printing operation to avoid unnecessary
// allocations. However, it is not safe for concurrent access.
type Printer struct {
err error // Sticky error value.
server *Server
dwarf *dwarf.Data
arch *arch.Architecture
printBuf bytes.Buffer // Accumulates the output.
visited map[typeAndAddress]bool // Prevents looping on cyclic data.
}
// printf prints to printBuf.
func (p *Printer) printf(format string, args ...interface{}) {
fmt.Fprintf(&p.printBuf, format, args...)
}
// errorf prints the error to printBuf, then sets the sticky error for the
// printer, if not already set.
func (p *Printer) errorf(format string, args ...interface{}) {
fmt.Fprintf(&p.printBuf, "<"+format+">", args...)
if p.err != nil {
return
}
p.err = fmt.Errorf(format, args...)
}
// NewPrinter returns a printer that can use the Server to access and print
// values of the specified architecture described by the provided DWARF data.
func NewPrinter(arch *arch.Architecture, dwarf *dwarf.Data, server *Server) *Printer {
return &Printer{
server: server,
arch: arch,
dwarf: dwarf,
visited: make(map[typeAndAddress]bool),
}
}
// reset resets the Printer. It must be called before starting a new
// printing operation.
func (p *Printer) reset() {
p.err = nil
p.printBuf.Reset()
// Just wipe the map rather than reallocating. It's almost always tiny.
for k := range p.visited {
delete(p.visited, k)
}
}
// Sprint returns the pretty-printed value of the item with the given name, such as "main.global".
func (p *Printer) Sprint(name string) (string, error) {
entry, err := p.dwarf.LookupEntry(name)
if err != nil {
return "", err
}
p.reset()
switch entry.Tag {
case dwarf.TagVariable: // TODO: What other entries have global location attributes?
var a uint64
iface := entry.Val(dwarf.AttrLocation)
if iface != nil {
a = p.decodeLocation(iface.([]byte))
}
p.printEntryValueAt(entry, a)
default:
p.errorf("unrecognized entry type %s", entry.Tag)
}
return p.printBuf.String(), p.err
}
// Figure 24 of DWARF v4.
const (
locationAddr = 0x03
)
// decodeLocation decodes the dwarf data describing an address.
func (p *Printer) decodeLocation(data []byte) uint64 {
switch data[0] {
case locationAddr:
return p.arch.Uintptr(data[1:])
default:
p.errorf("unimplemented location type %#x", data[0])
}
return 0
}
// SprintEntry returns the pretty-printed value of the item with the specified DWARF Entry and address.
func (p *Printer) SprintEntry(entry *dwarf.Entry, a uint64) (string, error) {
p.reset()
p.printEntryValueAt(entry, a)
return p.printBuf.String(), p.err
}
// printEntryValueAt pretty-prints the data at the specified address.
// using the type information in the Entry.
func (p *Printer) printEntryValueAt(entry *dwarf.Entry, a uint64) {
if a == 0 {
p.printf("<nil>")
return
}
switch entry.Tag {
case dwarf.TagVariable, dwarf.TagFormalParameter:
// OK
default:
p.errorf("unrecognized entry type %s", entry.Tag)
return
}
iface := entry.Val(dwarf.AttrType)
if iface == nil {
p.errorf("no type")
return
}
typ, err := p.dwarf.Type(iface.(dwarf.Offset))
if err != nil {
p.errorf("type lookup: %v", err)
return
}
p.printValueAt(typ, a)
}
// printValueAt pretty-prints the data at the specified address.
// using the provided type information.
func (p *Printer) printValueAt(typ dwarf.Type, a uint64) {
if a != 0 {
// Check if we are repeating the same type and address.
ta := typeAndAddress{typ, a}
if p.visited[ta] {
p.printf("(%v %#x)", typ, a)
return
}
p.visited[ta] = true
}
switch typ := typ.(type) {
case *dwarf.BoolType:
if typ.ByteSize != 1 {
p.errorf("unrecognized bool size %d", typ.ByteSize)
return
}
if b, err := p.server.peekUint8(a); err != nil {
p.errorf("reading bool: %s", err)
} else {
p.printf("%t", b != 0)
}
case *dwarf.PtrType:
if ptr, err := p.server.peekPtr(a); err != nil {
p.errorf("reading pointer: %s", err)
} else {
p.printf("%#x", ptr)
}
case *dwarf.IntType:
// Sad we can't tell a rune from an int32.
if i, err := p.server.peekInt(a, typ.ByteSize); err != nil {
p.errorf("reading integer: %s", err)
} else {
p.printf("%d", i)
}
case *dwarf.UintType:
if u, err := p.server.peekUint(a, typ.ByteSize); err != nil {
p.errorf("reading unsigned integer: %s", err)
} else {
p.printf("%d", u)
}
case *dwarf.FloatType:
buf := make([]byte, typ.ByteSize)
if err := p.server.peekBytes(a, buf); err != nil {
p.errorf("reading float: %s", err)
return
}
switch typ.ByteSize {
case 4:
p.printf("%g", p.arch.Float32(buf))
case 8:
p.printf("%g", p.arch.Float64(buf))
default:
p.errorf("unrecognized float size %d", typ.ByteSize)
}
case *dwarf.ComplexType:
buf := make([]byte, typ.ByteSize)
if err := p.server.peekBytes(a, buf); err != nil {
p.errorf("reading complex: %s", err)
return
}
switch typ.ByteSize {
case 8:
p.printf("%g", p.arch.Complex64(buf))
case 16:
p.printf("%g", p.arch.Complex128(buf))
default:
p.errorf("unrecognized complex size %d", typ.ByteSize)
}
case *dwarf.StructType:
if typ.Kind != "struct" {
// Could be "class" or "union".
p.errorf("can't handle struct type %s", typ.Kind)
return
}
p.printf("%s {", typ.String())
for i, field := range typ.Field {
if i != 0 {
p.printf(", ")
}
p.printValueAt(field.Type, a+uint64(field.ByteOffset))
}
p.printf("}")
case *dwarf.ArrayType:
p.printArrayAt(typ, a)
case *dwarf.InterfaceType:
p.printInterfaceAt(typ, a)
case *dwarf.MapType:
p.printMapAt(typ, a)
case *dwarf.ChanType:
p.printChannelAt(typ, a)
case *dwarf.SliceType:
p.printSliceAt(typ, a)
case *dwarf.StringType:
p.printStringAt(typ, a)
case *dwarf.TypedefType:
p.printValueAt(typ.Type, a)
case *dwarf.FuncType:
p.printf("%v @%#x ", typ, a)
case *dwarf.VoidType:
p.printf("void")
default:
p.errorf("unimplemented type %v", typ)
}
}
func (p *Printer) printArrayAt(typ *dwarf.ArrayType, a uint64) {
elemType := typ.Type
length := typ.Count
stride, ok := p.arrayStride(typ)
if !ok {
p.errorf("can't determine element size")
}
p.printf("%s{", typ)
n := length
if n > 100 {
n = 100 // TODO: Have a way to control this?
}
for i := int64(0); i < n; i++ {
if i != 0 {
p.printf(", ")
}
p.printValueAt(elemType, a)
a += stride // TODO: Alignment and padding - not given by Type
}
if n < length {
p.printf(", ...")
}
p.printf("}")
}
func (p *Printer) printInterfaceAt(t *dwarf.InterfaceType, a uint64) {
// t embeds a TypedefType, which may point to another typedef.
// The underlying type should be a struct.
st, ok := followTypedefs(&t.TypedefType).(*dwarf.StructType)
if !ok {
p.errorf("bad interface type: not a struct")
return
}
p.printf("(")
tab, err := p.server.peekPtrStructField(st, a, "tab")
if err != nil {
p.errorf("reading interface type: %s", err)
} else {
f, err := getField(st, "tab")
if err != nil {
p.errorf("%s", err)
} else {
p.printTypeOfInterface(f.Type, tab)
}
}
p.printf(", ")
data, err := p.server.peekPtrStructField(st, a, "data")
if err != nil {
p.errorf("reading interface value: %s", err)
} else if data == 0 {
p.printf("<nil>")
} else {
p.printf("%#x", data)
}
p.printf(")")
}
// printTypeOfInterface prints the type of the given tab pointer.
func (p *Printer) printTypeOfInterface(t dwarf.Type, a uint64) {
if a == 0 {
p.printf("<nil>")
return
}
// t should be a pointer to a struct containing _type, which is a pointer to a
// struct containing _string, which is the name of the type.
// Depending on the compiler version, some of these types can be typedefs, and
// _string may be a string or a *string.
t1, ok := followTypedefs(t).(*dwarf.PtrType)
if !ok {
p.errorf("interface's tab is not a pointer")
return
}
t2, ok := followTypedefs(t1.Type).(*dwarf.StructType)
if !ok {
p.errorf("interface's tab is not a pointer to struct")
return
}
typeField, err := getField(t2, "_type")
if err != nil {
p.errorf("%s", err)
return
}
t3, ok := followTypedefs(typeField.Type).(*dwarf.PtrType)
if !ok {
p.errorf("interface's _type is not a pointer")
return
}
t4, ok := followTypedefs(t3.Type).(*dwarf.StructType)
if !ok {
p.errorf("interface's _type is not a pointer to struct")
return
}
stringField, err := getField(t4, "_string")
if err != nil {
p.errorf("%s", err)
return
}
if t5, ok := stringField.Type.(*dwarf.PtrType); ok {
stringType, ok := t5.Type.(*dwarf.StringType)
if !ok {
p.errorf("interface _string is a pointer to %T, want string or *string", t5.Type)
return
}
typeAddr, err := p.server.peekPtrStructField(t2, a, "_type")
if err != nil {
p.errorf("reading interface type: %s", err)
return
}
stringAddr, err := p.server.peekPtrStructField(t4, typeAddr, "_string")
if err != nil {
p.errorf("reading interface type: %s", err)
return
}
p.printStringAt(stringType, stringAddr)
} else {
stringType, ok := stringField.Type.(*dwarf.StringType)
if !ok {
p.errorf("interface _string is a %T, want string or *string", stringField.Type)
return
}
typeAddr, err := p.server.peekPtrStructField(t2, a, "_type")
if err != nil {
p.errorf("reading interface type: %s", err)
return
}
stringAddr := typeAddr + uint64(stringField.ByteOffset)
p.printStringAt(stringType, stringAddr)
}
}
// maxMapValuesToPrint values are printed for each map; any remaining values are
// truncated to "...".
const maxMapValuesToPrint = 8
func (p *Printer) printMapAt(typ *dwarf.MapType, a uint64) {
count := 0
fn := func(keyAddr, valAddr uint64, keyType, valType dwarf.Type) (stop bool) {
count++
if count > maxMapValuesToPrint {
return false
}
if count > 1 {
p.printf(" ")
}
p.printValueAt(keyType, keyAddr)
p.printf(":")
p.printValueAt(valType, valAddr)
return true
}
p.printf("map[")
if err := p.server.peekMapValues(typ, a, fn); err != nil {
p.errorf("reading map values: %s", err)
}
if count > maxMapValuesToPrint {
p.printf(" ...")
}
p.printf("]")
}
func (p *Printer) printChannelAt(ct *dwarf.ChanType, a uint64) {
p.printf("(chan %s ", ct.ElemType)
defer p.printf(")")
a, err := p.server.peekPtr(a)
if err != nil {
p.errorf("reading channel: %s", err)
return
}
if a == 0 {
p.printf("<nil>")
return
}
p.printf("%#x", a)
// ct is a typedef for a pointer to a struct.
pt, ok := ct.TypedefType.Type.(*dwarf.PtrType)
if !ok {
p.errorf("bad channel type: not a pointer")
return
}
st, ok := pt.Type.(*dwarf.StructType)
if !ok {
p.errorf("bad channel type: not a pointer to a struct")
return
}
// Print the channel buffer's length (qcount) and capacity (dataqsiz),
// if not 0/0.
qcount, err := p.server.peekUintOrIntStructField(st, a, "qcount")
if err != nil {
p.errorf("reading channel: %s", err)
return
}
dataqsiz, err := p.server.peekUintOrIntStructField(st, a, "dataqsiz")
if err != nil {
p.errorf("reading channel: %s", err)
return
}
if qcount != 0 || dataqsiz != 0 {
p.printf(" [%d/%d]", qcount, dataqsiz)
}
}
func (p *Printer) printSliceAt(typ *dwarf.SliceType, a uint64) {
// Slices look like a struct with fields array *elemtype, len uint32/64, cap uint32/64.
// BUG: Slice header appears to have fields with ByteSize == 0
ptr, err := p.server.peekPtrStructField(&typ.StructType, a, "array")
if err != nil {
p.errorf("reading slice: %s", err)
return
}
length, err := p.server.peekUintOrIntStructField(&typ.StructType, a, "len")
if err != nil {
p.errorf("reading slice: %s", err)
return
}
// Capacity is not used yet.
_, err = p.server.peekUintOrIntStructField(&typ.StructType, a, "cap")
if err != nil {
p.errorf("reading slice: %s", err)
return
}
elemType := typ.ElemType
size, ok := p.sizeof(typ.ElemType)
if !ok {
p.errorf("can't determine element size")
}
p.printf("%s{", typ)
for i := uint64(0); i < length; i++ {
if i != 0 {
p.printf(", ")
}
p.printValueAt(elemType, ptr)
ptr += size // TODO: Alignment and padding - not given by Type
}
p.printf("}")
}
func (p *Printer) printStringAt(typ *dwarf.StringType, a uint64) {
const maxStringSize = 100
if s, err := p.server.peekString(typ, a, maxStringSize); err != nil {
p.errorf("reading string: %s", err)
} else {
p.printf("%q", s)
}
}
// sizeof returns the byte size of the type.
func (p *Printer) sizeof(typ dwarf.Type) (uint64, bool) {
size := typ.Size() // Will be -1 if ByteSize is not set.
if size >= 0 {
return uint64(size), true
}
switch typ.(type) {
case *dwarf.PtrType:
// This is the only one we know of, but more may arise.
return uint64(p.arch.PointerSize), true
}
return 0, false
}
// arrayStride returns the stride of a dwarf.ArrayType in bytes.
func (p *Printer) arrayStride(t *dwarf.ArrayType) (uint64, bool) {
stride := t.StrideBitSize
if stride > 0 {
return uint64(stride / 8), true
}
return p.sizeof(t.Type)
}
// getField finds the *dwarf.StructField in a dwarf.StructType with name fieldName.
func getField(t *dwarf.StructType, fieldName string) (*dwarf.StructField, error) {
var r *dwarf.StructField
for _, f := range t.Field {
if f.Name == fieldName {
if r != nil {
return nil, fmt.Errorf("struct definition repeats field %s", fieldName)
}
r = f
}
}
if r == nil {
return nil, fmt.Errorf("struct field %s missing", fieldName)
}
return r, nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/wangHvip/google-cloud-go.git
git@gitee.com:wangHvip/google-cloud-go.git
wangHvip
google-cloud-go
google-cloud-go
v0.34.0

搜索帮助