github.com/golangci/go-tools@v0.0.0-20190318060251-af6baa5dc196/cmd/structlayout/main.go (about) 1 // structlayout displays the layout (field sizes and padding) of structs. 2 package main 3 4 import ( 5 "encoding/json" 6 "flag" 7 "fmt" 8 "go/build" 9 "go/types" 10 "log" 11 "os" 12 13 "github.com/golangci/go-tools/gcsizes" 14 st "github.com/golangci/go-tools/structlayout" 15 "github.com/golangci/go-tools/version" 16 17 "golang.org/x/tools/go/loader" 18 ) 19 20 var ( 21 fJSON bool 22 fVersion bool 23 ) 24 25 func init() { 26 flag.BoolVar(&fJSON, "json", false, "Format data as JSON") 27 flag.BoolVar(&fVersion, "version", false, "Print version and exit") 28 } 29 30 func main() { 31 log.SetFlags(0) 32 flag.Parse() 33 34 if fVersion { 35 version.Print() 36 os.Exit(0) 37 } 38 39 if len(flag.Args()) != 2 { 40 flag.Usage() 41 os.Exit(1) 42 } 43 44 conf := loader.Config{ 45 Build: &build.Default, 46 } 47 48 var pkg string 49 var typName string 50 pkg = flag.Args()[0] 51 typName = flag.Args()[1] 52 conf.Import(pkg) 53 54 lprog, err := conf.Load() 55 if err != nil { 56 log.Fatal(err) 57 } 58 var typ types.Type 59 obj := lprog.Package(pkg).Pkg.Scope().Lookup(typName) 60 if obj == nil { 61 log.Fatal("couldn't find type") 62 } 63 typ = obj.Type() 64 65 st, ok := typ.Underlying().(*types.Struct) 66 if !ok { 67 log.Fatal("identifier is not a struct type") 68 } 69 70 fields := sizes(st, typ.(*types.Named).Obj().Name(), 0, nil) 71 if fJSON { 72 emitJSON(fields) 73 } else { 74 emitText(fields) 75 } 76 } 77 78 func emitJSON(fields []st.Field) { 79 if fields == nil { 80 fields = []st.Field{} 81 } 82 json.NewEncoder(os.Stdout).Encode(fields) 83 } 84 85 func emitText(fields []st.Field) { 86 for _, field := range fields { 87 fmt.Println(field) 88 } 89 } 90 func sizes(typ *types.Struct, prefix string, base int64, out []st.Field) []st.Field { 91 s := gcsizes.ForArch(build.Default.GOARCH) 92 n := typ.NumFields() 93 var fields []*types.Var 94 for i := 0; i < n; i++ { 95 fields = append(fields, typ.Field(i)) 96 } 97 offsets := s.Offsetsof(fields) 98 for i := range offsets { 99 offsets[i] += base 100 } 101 102 pos := base 103 for i, field := range fields { 104 if offsets[i] > pos { 105 padding := offsets[i] - pos 106 out = append(out, st.Field{ 107 IsPadding: true, 108 Start: pos, 109 End: pos + padding, 110 Size: padding, 111 }) 112 pos += padding 113 } 114 size := s.Sizeof(field.Type()) 115 if typ2, ok := field.Type().Underlying().(*types.Struct); ok && typ2.NumFields() != 0 { 116 out = sizes(typ2, prefix+"."+field.Name(), pos, out) 117 } else { 118 out = append(out, st.Field{ 119 Name: prefix + "." + field.Name(), 120 Type: field.Type().String(), 121 Start: offsets[i], 122 End: offsets[i] + size, 123 Size: size, 124 Align: s.Alignof(field.Type()), 125 }) 126 } 127 pos += size 128 } 129 130 if len(out) == 0 { 131 return out 132 } 133 field := &out[len(out)-1] 134 if field.Size == 0 { 135 field.Size = 1 136 field.End++ 137 } 138 pad := s.Sizeof(typ) - field.End 139 if pad > 0 { 140 out = append(out, st.Field{ 141 IsPadding: true, 142 Start: field.End, 143 End: field.End + pad, 144 Size: pad, 145 }) 146 } 147 148 return out 149 }