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\">&#9875;</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\">&#9875;</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  }