// Copyright 2018 The gVisor Authors. // // 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. // Package state provides functionality related to saving and loading object // graphs. For most types, it provides a set of default saving / loading logic // that will be invoked automatically if custom logic is not defined. // // Kind Support // ---- ------- // Bool default // Int default // Int8 default // Int16 default // Int32 default // Int64 default // Uint default // Uint8 default // Uint16 default // Uint32 default // Uint64 default // Float32 default // Float64 default // Complex64 default // Complex128 default // Array default // Chan custom // Func custom // Interface default // Map default // Ptr default // Slice default // String default // Struct custom (*) Unless zero-sized. // UnsafePointer custom // // See README.md for an overview of how encoding and decoding works. package state import ( "context" "fmt" "reflect" "runtime" "gvisor.dev/gvisor/pkg/state/wire" ) // objectID is a unique identifier assigned to each object to be serialized. // Each instance of an object is considered separately, i.e. if there are two // objects of the same type in the object graph being serialized, they'll be // assigned unique objectIDs. type objectID uint32 // typeID is the identifier for a type. Types are serialized and tracked // alongside objects in order to avoid the overhead of encoding field names in // all objects. type typeID uint32 // ErrState is returned when an error is encountered during encode/decode. type ErrState struct { // err is the underlying error. err error // trace is the stack trace. trace string } // Error returns a sensible description of the state error. func (e *ErrState) Error() string { return fmt.Sprintf("%v:\n%s", e.err, e.trace) } // Unwrap implements standard unwrapping. func (e *ErrState) Unwrap() error { return e.err } // Save saves the given object state. func Save(ctx context.Context, w wire.Writer, rootPtr interface{}) (Stats, error) { // Create the encoding state. es := encodeState{ ctx: ctx, w: w, types: makeTypeEncodeDatabase(), zeroValues: make(map[reflect.Type]*objectEncodeState), pending: make(map[objectID]*objectEncodeState), encodedStructs: make(map[reflect.Value]*wire.Struct), } // Perform the encoding. err := safely(func() { es.Save(reflect.ValueOf(rootPtr).Elem()) }) return es.stats, err } // Load loads a checkpoint. func Load(ctx context.Context, r wire.Reader, rootPtr interface{}) (Stats, error) { // Create the decoding state. ds := decodeState{ ctx: ctx, r: r, types: makeTypeDecodeDatabase(), deferred: make(map[objectID]wire.Object), } // Attempt our decode. err := safely(func() { ds.Load(reflect.ValueOf(rootPtr).Elem()) }) return ds.stats, err } // Sink is used for Type.StateSave. type Sink struct { internal objectEncoder } // Save adds the given object to the map. // // You should pass always pointers to the object you are saving. For example: // // type X struct { // A int // B *int // } // // func (x *X) StateTypeInfo(m Sink) state.TypeInfo { // return state.TypeInfo{ // Name: "pkg.X", // Fields: []string{ // "A", // "B", // }, // } // } // // func (x *X) StateSave(m Sink) { // m.Save(0, &x.A) // Field is A. // m.Save(1, &x.B) // Field is B. // } // // func (x *X) StateLoad(m Source) { // m.Load(0, &x.A) // Field is A. // m.Load(1, &x.B) // Field is B. // } func (s Sink) Save(slot int, objPtr interface{}) { s.internal.save(slot, reflect.ValueOf(objPtr).Elem()) } // SaveValue adds the given object value to the map. // // This should be used for values where pointers are not available, or casts // are required during Save/Load. // // For example, if we want to cast external package type P.Foo to int64: // // func (x *X) StateSave(m Sink) { // m.SaveValue(0, "A", int64(x.A)) // } // // func (x *X) StateLoad(m Source) { // m.LoadValue(0, new(int64), func(x interface{}) { // x.A = P.Foo(x.(int64)) // }) // } func (s Sink) SaveValue(slot int, obj interface{}) { s.internal.save(slot, reflect.ValueOf(obj)) } // Context returns the context object provided at save time. func (s Sink) Context() context.Context { return s.internal.es.ctx } // Type is an interface that must be implemented by Struct objects. This allows // these objects to be serialized while minimizing runtime reflection required. // // All these methods can be automatically generated by the go_statify tool. type Type interface { // StateTypeName returns the type's name. // // This is used for matching type information during encoding and // decoding, as well as dynamic interface dispatch. This should be // globally unique. StateTypeName() string // StateFields returns information about the type. // // Fields is the set of fields for the object. Calls to Sink.Save and // Source.Load must be made in-order with respect to these fields. // // This will be called at most once per serialization. StateFields() []string } // SaverLoader must be implemented by struct types. type SaverLoader interface { // StateSave saves the state of the object to the given Map. StateSave(Sink) // StateLoad loads the state of the object. StateLoad(Source) } // Source is used for Type.StateLoad. type Source struct { internal objectDecoder } // Load loads the given object passed as a pointer.. // // See Sink.Save for an example. func (s Source) Load(slot int, objPtr interface{}) { s.internal.load(slot, reflect.ValueOf(objPtr), false, nil) } // LoadWait loads the given objects from the map, and marks it as requiring all // AfterLoad executions to complete prior to running this object's AfterLoad. // // See Sink.Save for an example. func (s Source) LoadWait(slot int, objPtr interface{}) { s.internal.load(slot, reflect.ValueOf(objPtr), true, nil) } // LoadValue loads the given object value from the map. // // See Sink.SaveValue for an example. func (s Source) LoadValue(slot int, objPtr interface{}, fn func(interface{})) { o := reflect.ValueOf(objPtr) s.internal.load(slot, o, true, func() { fn(o.Elem().Interface()) }) } // AfterLoad schedules a function execution when all objects have been // allocated and their automated loading and customized load logic have been // executed. fn will not be executed until all of current object's // dependencies' AfterLoad() logic, if exist, have been executed. func (s Source) AfterLoad(fn func()) { s.internal.afterLoad(fn) } // Context returns the context object provided at load time. func (s Source) Context() context.Context { return s.internal.ds.ctx } // IsZeroValue checks if the given value is the zero value. // // This function is used by the stateify tool. func IsZeroValue(val interface{}) bool { return val == nil || reflect.ValueOf(val).Elem().IsZero() } // Failf is a wrapper around panic that should be used to generate errors that // can be caught during saving and loading. func Failf(fmtStr string, v ...interface{}) { panic(fmt.Errorf(fmtStr, v...)) } // safely executes the given function, catching a panic and unpacking as an // error. // // The error flow through the state package uses panic and recover. There are // two important reasons for this: // // 1) Many of the reflection methods will already panic with invalid data or // violated assumptions. We would want to recover anyways here. // // 2) It allows us to eliminate boilerplate within Save() and Load() functions. // In nearly all cases, when the low-level serialization functions fail, you // will want the checkpoint to fail anyways. Plumbing errors through every // method doesn't add a lot of value. If there are specific error conditions // that you'd like to handle, you should add appropriate functionality to // objects themselves prior to calling Save() and Load(). func safely(fn func()) (err error) { defer func() { if r := recover(); r != nil { if es, ok := r.(*ErrState); ok { err = es // Propagate. return } // Build a new state error. es := new(ErrState) if e, ok := r.(error); ok { es.err = e } else { es.err = fmt.Errorf("%v", r) } // Make a stack. We don't know how big it will be ahead // of time, but want to make sure we get the whole // thing. So we just do a stupid brute force approach. var stack []byte for sz := 1024; ; sz *= 2 { stack = make([]byte, sz) n := runtime.Stack(stack, false) if n < sz { es.trace = string(stack[:n]) break } } // Set the error. err = es } }() // Execute the function. fn() return nil }