github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/state/pretty/pretty.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package pretty is a pretty-printer for state streams. 16 package pretty 17 18 import ( 19 "fmt" 20 "io" 21 "io/ioutil" 22 "reflect" 23 "strings" 24 25 "github.com/ttpreport/gvisor-ligolo/pkg/state" 26 "github.com/ttpreport/gvisor-ligolo/pkg/state/wire" 27 ) 28 29 type printer struct { 30 html bool 31 typeSpecs map[string]*wire.Type 32 } 33 34 func (p *printer) formatRef(x *wire.Ref, graph uint64) string { 35 baseRef := fmt.Sprintf("g%dr%d", graph, x.Root) 36 fullRef := baseRef 37 if len(x.Dots) > 0 { 38 // See wire.Ref; Type valid if Dots non-zero. 39 typ, _ := p.formatType(x.Type, graph) 40 var buf strings.Builder 41 buf.WriteString("(*") 42 buf.WriteString(typ) 43 buf.WriteString(")(") 44 buf.WriteString(baseRef) 45 buf.WriteString(")") 46 for _, component := range x.Dots { 47 switch v := component.(type) { 48 case *wire.FieldName: 49 buf.WriteString(".") 50 buf.WriteString(string(*v)) 51 case wire.Index: 52 buf.WriteString(fmt.Sprintf("[%d]", v)) 53 default: 54 panic(fmt.Sprintf("unreachable: switch should be exhaustive, unhandled case %v", reflect.TypeOf(component))) 55 } 56 } 57 fullRef = buf.String() 58 } 59 if p.html { 60 return fmt.Sprintf("<a href=\"#%s\">%s</a>", baseRef, fullRef) 61 } 62 return fullRef 63 } 64 65 func (p *printer) formatType(t wire.TypeSpec, graph uint64) (string, bool) { 66 switch x := t.(type) { 67 case wire.TypeID: 68 tag := fmt.Sprintf("g%dt%d", graph, x) 69 desc := tag 70 if spec, ok := p.typeSpecs[tag]; ok { 71 desc += fmt.Sprintf("=%s", spec.Name) 72 } else { 73 desc += "!missing-type-spec" 74 } 75 if p.html { 76 return fmt.Sprintf("<a href=\"#%s\">%s</a>", tag, desc), true 77 } 78 return desc, true 79 case wire.TypeSpecNil: 80 return "", false // Only nil type. 81 case *wire.TypeSpecPointer: 82 element, _ := p.formatType(x.Type, graph) 83 return fmt.Sprintf("(*%s)", element), true 84 case *wire.TypeSpecArray: 85 element, _ := p.formatType(x.Type, graph) 86 return fmt.Sprintf("[%d](%s)", x.Count, element), true 87 case *wire.TypeSpecSlice: 88 element, _ := p.formatType(x.Type, graph) 89 return fmt.Sprintf("([]%s)", element), true 90 case *wire.TypeSpecMap: 91 key, _ := p.formatType(x.Key, graph) 92 value, _ := p.formatType(x.Value, graph) 93 return fmt.Sprintf("(map[%s]%s)", key, value), true 94 default: 95 panic(fmt.Sprintf("unreachable: unknown type %T", t)) 96 } 97 } 98 99 // format formats a single object, for pretty-printing. It also returns whether 100 // the value is a non-zero value. 101 func (p *printer) format(graph uint64, depth int, encoded wire.Object) (string, bool) { 102 switch x := encoded.(type) { 103 case wire.Nil: 104 return "nil", false 105 case *wire.String: 106 return fmt.Sprintf("%q", *x), *x != "" 107 case *wire.Complex64: 108 return fmt.Sprintf("%f+%fi", real(*x), imag(*x)), *x != 0.0 109 case *wire.Complex128: 110 return fmt.Sprintf("%f+%fi", real(*x), imag(*x)), *x != 0.0 111 case *wire.Ref: 112 return p.formatRef(x, graph), x.Root != 0 113 case *wire.Type: 114 tabs := "\n" + strings.Repeat("\t", depth) 115 items := make([]string, 0, len(x.Fields)+2) 116 items = append(items, fmt.Sprintf("type %s {", x.Name)) 117 for i := 0; i < len(x.Fields); i++ { 118 items = append(items, fmt.Sprintf("\t%d: %s,", i, x.Fields[i])) 119 } 120 items = append(items, "}") 121 return strings.Join(items, tabs), true // No zero value. 122 case *wire.Slice: 123 return fmt.Sprintf("%s{len:%d,cap:%d}", p.formatRef(&x.Ref, graph), x.Length, x.Capacity), x.Capacity != 0 124 case *wire.Array: 125 if len(x.Contents) == 0 { 126 return "[]", false 127 } 128 items := make([]string, 0, len(x.Contents)+2) 129 zeros := make([]string, 0) // used to eliminate zero entries. 130 items = append(items, "[") 131 tabs := "\n" + strings.Repeat("\t", depth) 132 for i := 0; i < len(x.Contents); i++ { 133 item, ok := p.format(graph, depth+1, x.Contents[i]) 134 if !ok { 135 zeros = append(zeros, fmt.Sprintf("\t%s,", item)) 136 continue 137 } 138 if len(zeros) > 0 { 139 items = append(items, zeros...) 140 zeros = nil 141 } 142 items = append(items, fmt.Sprintf("\t%s,", item)) 143 } 144 if len(zeros) > 0 { 145 items = append(items, fmt.Sprintf("\t... (%d zeros),", len(zeros))) 146 } 147 items = append(items, "]") 148 return strings.Join(items, tabs), len(zeros) < len(x.Contents) 149 case *wire.Struct: 150 tag := fmt.Sprintf("g%dt%d", graph, x.TypeID) 151 spec, _ := p.typeSpecs[tag] 152 typ, _ := p.formatType(x.TypeID, graph) 153 if x.Fields() == 0 { 154 return fmt.Sprintf("struct[%s]{}", typ), false 155 } 156 items := make([]string, 0, 2) 157 items = append(items, fmt.Sprintf("struct[%s]{", typ)) 158 tabs := "\n" + strings.Repeat("\t", depth) 159 allZero := true 160 for i := 0; i < x.Fields(); i++ { 161 var name string 162 if spec != nil && i < len(spec.Fields) { 163 name = spec.Fields[i] 164 } else { 165 name = fmt.Sprintf("%d", i) 166 } 167 element, ok := p.format(graph, depth+1, *x.Field(i)) 168 allZero = allZero && !ok 169 items = append(items, fmt.Sprintf("\t%s: %s,", name, element)) 170 } 171 items = append(items, "}") 172 return strings.Join(items, tabs), !allZero 173 case *wire.Map: 174 if len(x.Keys) == 0 { 175 return "map{}", false 176 } 177 items := make([]string, 0, len(x.Keys)+2) 178 items = append(items, "map{") 179 tabs := "\n" + strings.Repeat("\t", depth) 180 for i := 0; i < len(x.Keys); i++ { 181 key, _ := p.format(graph, depth+1, x.Keys[i]) 182 value, _ := p.format(graph, depth+1, x.Values[i]) 183 items = append(items, fmt.Sprintf("\t%s: %s,", key, value)) 184 } 185 items = append(items, "}") 186 return strings.Join(items, tabs), true 187 case *wire.Interface: 188 typ, typOk := p.formatType(x.Type, graph) 189 element, elementOk := p.format(graph, depth+1, x.Value) 190 return fmt.Sprintf("interface[%s]{%s}", typ, element), typOk || elementOk 191 default: 192 // Must be a primitive; use reflection. 193 return fmt.Sprintf("%v", encoded), true 194 } 195 } 196 197 // printStream is the basic print implementation. 198 func (p *printer) printStream(w io.Writer, r wire.Reader) (err error) { 199 // current graph ID. 200 var graph uint64 201 202 if p.html { 203 fmt.Fprintf(w, "<pre>") 204 defer fmt.Fprintf(w, "</pre>") 205 } 206 207 defer func() { 208 if r := recover(); r != nil { 209 if rErr, ok := r.(error); ok { 210 err = rErr // Override return. 211 return 212 } 213 panic(r) // Propagate. 214 } 215 }() 216 217 p.typeSpecs = make(map[string]*wire.Type) 218 219 for { 220 // Find the first object to begin generation. 221 length, object, err := state.ReadHeader(r) 222 if err == io.EOF { 223 // Nothing else to do. 224 break 225 } else if err != nil { 226 return err 227 } 228 if !object { 229 graph++ // Increment the graph. 230 if length > 0 { 231 fmt.Fprintf(w, "(%d bytes non-object data)\n", length) 232 io.Copy(ioutil.Discard, &io.LimitedReader{ 233 R: r, 234 N: int64(length), 235 }) 236 } 237 continue 238 } 239 240 // Read & unmarshal the object. 241 // 242 // Note that this loop must match the general structure of the 243 // loop in decode.go. But we don't register type information, 244 // etc. and just print the raw structures. 245 type objectAndID struct { 246 id uint64 247 obj wire.Object 248 } 249 var ( 250 tid uint64 = 1 251 objects []objectAndID 252 ) 253 for i := uint64(0); i < length; { 254 // Unmarshal either a type object or object ID. 255 encoded := wire.Load(r) 256 switch we := encoded.(type) { 257 case *wire.Type: 258 str, _ := p.format(graph, 0, encoded) 259 tag := fmt.Sprintf("g%dt%d", graph, tid) 260 p.typeSpecs[tag] = we 261 if p.html { 262 // See below. 263 tag = fmt.Sprintf("<a name=\"%s\">%s</a><a href=\"#%s\">⚓</a>", tag, tag, tag) 264 } 265 if _, err := fmt.Fprintf(w, "%s = %s\n", tag, str); err != nil { 266 return err 267 } 268 tid++ 269 case wire.Uint: 270 // Unmarshal the actual object. 271 objects = append(objects, objectAndID{ 272 id: uint64(we), 273 obj: wire.Load(r), 274 }) 275 i++ 276 default: 277 return fmt.Errorf("wanted type or object ID, got %#v", encoded) 278 } 279 } 280 281 for _, objAndID := range objects { 282 // Format the node. 283 str, _ := p.format(graph, 0, objAndID.obj) 284 tag := fmt.Sprintf("g%dr%d", graph, objAndID.id) 285 if p.html { 286 // Create a little tag with an anchor next to it for linking. 287 tag = fmt.Sprintf("<a name=\"%s\">%s</a><a href=\"#%s\">⚓</a>", tag, tag, tag) 288 } 289 if _, err := fmt.Fprintf(w, "%s = %s\n", tag, str); err != nil { 290 return err 291 } 292 } 293 } 294 295 return nil 296 } 297 298 // PrintText reads the stream from r and prints text to w. 299 func PrintText(w io.Writer, r wire.Reader) error { 300 return (&printer{}).printStream(w, r) 301 } 302 303 // PrintHTML reads the stream from r and prints html to w. 304 func PrintHTML(w io.Writer, r wire.Reader) error { 305 return (&printer{html: true}).printStream(w, r) 306 }