internal/kobject: improve error JSON representation
All checks were successful
Test / Create distribution (push) Successful in 1m4s
Test / Sandbox (push) Successful in 2m55s
Test / Hakurei (push) Successful in 3m52s
Test / ShareFS (push) Successful in 3m44s
Test / Sandbox (race detector) (push) Successful in 5m25s
Test / Hakurei (race detector) (push) Successful in 6m35s
Test / Flake checks (push) Successful in 1m22s
All checks were successful
Test / Create distribution (push) Successful in 1m4s
Test / Sandbox (push) Successful in 2m55s
Test / Hakurei (push) Successful in 3m52s
Test / ShareFS (push) Successful in 3m44s
Test / Sandbox (race detector) (push) Successful in 5m25s
Test / Hakurei (race detector) (push) Successful in 6m35s
Test / Flake checks (push) Successful in 1m22s
This is now usable by internal/report. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"hakurei.app/internal/report"
|
||||
"hakurei.app/internal/uevent"
|
||||
)
|
||||
|
||||
@@ -29,9 +30,9 @@ const (
|
||||
// Object represents a kernel object.
|
||||
type Object struct {
|
||||
// Origin of the object.
|
||||
State int `json:"-"`
|
||||
State int `json:"state,omitempty"`
|
||||
// Set by [uevent.KOBJ_OFFLINE] and [uevent.KOBJ_ONLINE].
|
||||
Offline bool
|
||||
Offline bool `json:"offline,omitempty"`
|
||||
|
||||
// alloc_uevent_skb: devpath
|
||||
DevPath string `json:"devpath"`
|
||||
@@ -48,11 +49,11 @@ type Object struct {
|
||||
Env map[string]string `json:"env"`
|
||||
}
|
||||
|
||||
// Clone returns a copy of o.
|
||||
func (o *Object) Clone() Object {
|
||||
// Clone returns the address of a copy of o.
|
||||
func (o *Object) Clone() *Object {
|
||||
v := *o
|
||||
v.Env = maps.Clone(o.Env)
|
||||
return v
|
||||
return &v
|
||||
}
|
||||
|
||||
// GoString returns compound literal for the underlying value.
|
||||
@@ -194,77 +195,94 @@ func (s *State) Range(ctx context.Context, f func(o *Object) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// UnexpectedColdbootError is reported by [State.Consume] for a coldboot event
|
||||
// with action other than the expected [uevent.KOBJ_ADD].
|
||||
type UnexpectedColdbootError Event
|
||||
|
||||
func (e UnexpectedColdbootError) Error() string {
|
||||
return "unexpected " + e.Action.String() + " coldboot event"
|
||||
// An EventError describes a malformed or inconsistent [Event].
|
||||
type EventError struct {
|
||||
Kind int `json:"fault"`
|
||||
E Event `json:"event"`
|
||||
O *Object `json:"object,omitempty"`
|
||||
}
|
||||
|
||||
// DuplicateAddError is reported by [State.Consume] for a [uevent.KOBJ_ADD]
|
||||
// event on a still-existing entry that was not the result of a coldboot.
|
||||
type DuplicateAddError Event
|
||||
var _ report.RepresentableError = EventError{}
|
||||
|
||||
func (e DuplicateAddError) Error() string {
|
||||
return "duplicate add event on devpath " + strconv.Quote(e.DevPath)
|
||||
}
|
||||
func (EventError) Representable() {}
|
||||
|
||||
// TargetError is reported by [State.Consume] for an event on a nonexistent
|
||||
// entry. This is generally only possible before coldboot completes.
|
||||
type TargetError Event
|
||||
const (
|
||||
// EUnexpectedColdboot is reported for a coldboot event with action other
|
||||
// than the expected [uevent.KOBJ_ADD].
|
||||
EUnexpectedColdboot = iota
|
||||
// EDuplicateAdd is reported for a [uevent.KOBJ_ADD] event on a
|
||||
// still-existing entry that was not the result of a coldboot.
|
||||
EDuplicateAdd
|
||||
// EBadTarget is reported for an event on a nonexistent [Object]. This is
|
||||
// generally only possible before coldboot completes.
|
||||
EBadTarget
|
||||
// ERemoveState is reported for a [uevent.KOBJ_REMOVE] event targeting an
|
||||
// entry in a state other than [StateColdboot] and [StateNew].
|
||||
ERemoveState
|
||||
// EUnexpectedOffline is reported for a [uevent.KOBJ_OFFLINE] or
|
||||
// [uevent.KOBJ_ONLINE] event targeting an already offline or online object.
|
||||
EUnexpectedOffline
|
||||
// EBindState is reported for a [uevent.KOBJ_BIND] event targeting an entry
|
||||
// in a state other than [StateColdboot] and [StateNew].
|
||||
EBindState
|
||||
// EUnbindState is reported for a [uevent.KOBJ_UNBIND] event targeting an
|
||||
// entry in a state other than [StateBound].
|
||||
EUnbindState
|
||||
// EMalformedMove is reported for a [uevent.KOBJ_MOVE] event missing the
|
||||
// DEVPATH_OLD environment variable.
|
||||
EMalformedMove
|
||||
)
|
||||
|
||||
func (e TargetError) Error() string {
|
||||
return "unexpected " + e.Action.String() +
|
||||
" event on devpath " + strconv.Quote(e.DevPath)
|
||||
}
|
||||
func (e EventError) Error() string {
|
||||
switch e.Kind {
|
||||
case EUnexpectedColdboot:
|
||||
return "unexpected " + e.E.Action.String() + " coldboot event"
|
||||
case EDuplicateAdd:
|
||||
return "duplicate add event on devpath " + strconv.Quote(e.E.DevPath)
|
||||
case EBadTarget:
|
||||
return "unexpected " + e.E.Action.String() + " event on devpath " +
|
||||
strconv.Quote(e.E.DevPath)
|
||||
case ERemoveState:
|
||||
if e.O == nil {
|
||||
return "invalid remove event error"
|
||||
}
|
||||
return "remove event targeting devpath " + strconv.Quote(e.E.DevPath) +
|
||||
" in state " + strconv.Itoa(e.O.State)
|
||||
case EUnexpectedOffline:
|
||||
if e.O == nil {
|
||||
return "invalid unexpected offline error"
|
||||
}
|
||||
if e.O.Offline {
|
||||
return "offline event targeting devpath " + strconv.Quote(e.E.DevPath)
|
||||
}
|
||||
return "online event targeting devpath " + strconv.Quote(e.E.DevPath)
|
||||
case EBindState:
|
||||
if e.O == nil {
|
||||
return "invalid bind state error"
|
||||
}
|
||||
return "bind event targeting devpath " + strconv.Quote(e.E.DevPath) +
|
||||
" in state " + strconv.Itoa(e.O.State)
|
||||
case EUnbindState:
|
||||
if e.O == nil {
|
||||
return "invalid unbind state error"
|
||||
}
|
||||
return "unbind event targeting devpath " + strconv.Quote(e.E.DevPath) +
|
||||
" in state " + strconv.Itoa(e.O.State)
|
||||
case EMalformedMove:
|
||||
return "move event targeting devpath " + strconv.Quote(e.E.DevPath) +
|
||||
" missing DEVPATH_OLD"
|
||||
|
||||
// RemoveStateError is reported by [State.Consume] for a [uevent.KOBJ_REMOVE]
|
||||
// event targeting an entry in a state other than [StateColdboot] and [StateNew].
|
||||
type RemoveStateError Object
|
||||
|
||||
func (e RemoveStateError) Error() string {
|
||||
return "remove event targeting devpath " + strconv.Quote(e.DevPath) +
|
||||
" in state " + strconv.Itoa(e.State)
|
||||
}
|
||||
|
||||
// BindStateError is reported by [State.Consume] for a [uevent.KOBJ_BIND] event
|
||||
// targeting an entry in a state other than [StateColdboot] and [StateNew].
|
||||
type BindStateError Object
|
||||
|
||||
func (e BindStateError) Error() string {
|
||||
return "bind event targeting devpath " + strconv.Quote(e.DevPath) +
|
||||
" in state " + strconv.Itoa(e.State)
|
||||
}
|
||||
|
||||
// UnbindStateError is reported by [State.Consume] for a [uevent.KOBJ_UNBIND]
|
||||
// event targeting an entry in a state other than [StateBound].
|
||||
type UnbindStateError Object
|
||||
|
||||
func (e UnbindStateError) Error() string {
|
||||
return "unbind event targeting devpath " + strconv.Quote(e.DevPath) +
|
||||
" in state " + strconv.Itoa(e.State)
|
||||
}
|
||||
|
||||
// MalformedMoveError is reported by [State.Consume] for a [uevent.KOBJ_MOVE]
|
||||
// event missing the DEVPATH_OLD environment variable.
|
||||
type MalformedMoveError Event
|
||||
|
||||
func (e MalformedMoveError) Error() string {
|
||||
return "move event targeting devpath " + strconv.Quote(e.DevPath) +
|
||||
" missing DEVPATH_OLD"
|
||||
}
|
||||
|
||||
// UnexpectedOfflineError is reported by [State.Consume] for a
|
||||
// [uevent.KOBJ_OFFLINE] or [uevent.KOBJ_ONLINE] event targeting an already
|
||||
// offline or online object.
|
||||
type UnexpectedOfflineError Object
|
||||
|
||||
func (e UnexpectedOfflineError) Error() string {
|
||||
if e.Offline {
|
||||
return "offline event targeting devpath " + strconv.Quote(e.DevPath)
|
||||
default:
|
||||
return "invalid event error kind " + strconv.Itoa(e.Kind)
|
||||
}
|
||||
return "online event targeting devpath " + strconv.Quote(e.DevPath)
|
||||
}
|
||||
|
||||
// NewError returns a new [EventError] for e and o.
|
||||
func (e *Event) NewError(kind int, o *Object) error {
|
||||
if o != nil {
|
||||
o = o.Clone()
|
||||
}
|
||||
return EventError{kind, e.Clone(), o}
|
||||
}
|
||||
|
||||
// processEvent merges an event into s.
|
||||
@@ -274,7 +292,7 @@ func (s *State) processEvent(e *Event) {
|
||||
|
||||
coldboot := e.Synth != nil
|
||||
if e.Action != uevent.KOBJ_ADD && coldboot {
|
||||
s.reportErr(UnexpectedColdbootError(e.Clone()))
|
||||
s.reportErr(e.NewError(EUnexpectedColdboot, nil))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -282,7 +300,7 @@ func (s *State) processEvent(e *Event) {
|
||||
case uevent.KOBJ_ADD:
|
||||
if e.Synth == nil {
|
||||
if o, ok := s.uevent[e.DevPath]; ok {
|
||||
s.reportErr(DuplicateAddError(e.Clone()))
|
||||
s.reportErr(e.NewError(EDuplicateAdd, o))
|
||||
o.merge(e.Env)
|
||||
s.dispatchIter(o)
|
||||
return
|
||||
@@ -299,10 +317,10 @@ func (s *State) processEvent(e *Event) {
|
||||
|
||||
case uevent.KOBJ_REMOVE:
|
||||
if o, ok := s.uevent[e.DevPath]; !ok {
|
||||
s.reportErr(TargetError(e.Clone()))
|
||||
s.reportErr(e.NewError(EBadTarget, nil))
|
||||
return
|
||||
} else if o.State != StateColdboot && o.State != StateNew {
|
||||
s.reportErr(RemoveStateError(o.Clone()))
|
||||
s.reportErr(e.NewError(ERemoveState, o))
|
||||
}
|
||||
delete(s.uevent, e.DevPath)
|
||||
return
|
||||
@@ -310,7 +328,7 @@ func (s *State) processEvent(e *Event) {
|
||||
case uevent.KOBJ_CHANGE:
|
||||
o, ok := s.uevent[e.DevPath]
|
||||
if !ok {
|
||||
s.reportErr(TargetError(e.Clone()))
|
||||
s.reportErr(e.NewError(EBadTarget, nil))
|
||||
// this suffers from the coldboot race window similar to KOBJ_MOVE,
|
||||
// however this action combines driver-specific and change-specific
|
||||
// environment variables and combines them with environment
|
||||
@@ -333,11 +351,11 @@ func (s *State) processEvent(e *Event) {
|
||||
case uevent.KOBJ_MOVE:
|
||||
var o *Object
|
||||
if old, ok := e.Env["DEVPATH_OLD"]; !ok {
|
||||
s.reportErr(MalformedMoveError(e.Clone()))
|
||||
s.reportErr(e.NewError(EMalformedMove, nil))
|
||||
// not reached
|
||||
o = e.makeColdboot()
|
||||
} else if o, ok = s.uevent[old]; !ok {
|
||||
s.reportErr(TargetError(e.Clone()))
|
||||
s.reportErr(e.NewError(EBadTarget, nil))
|
||||
// this generally happens during coldboot, dropping the event here
|
||||
// may cause inconsistent state if the coldboot event for this
|
||||
// object was generated before the bind event
|
||||
@@ -356,14 +374,14 @@ func (s *State) processEvent(e *Event) {
|
||||
case uevent.KOBJ_ONLINE:
|
||||
o, ok := s.uevent[e.DevPath]
|
||||
if !ok {
|
||||
s.reportErr(TargetError(e.Clone()))
|
||||
s.reportErr(e.NewError(EBadTarget, nil))
|
||||
// coldboot race window similar to an unexpected KOBJ_MOVE
|
||||
o = e.makeColdboot()
|
||||
s.uevent[e.DevPath] = o
|
||||
o.merge(e.Env)
|
||||
}
|
||||
if !o.Offline {
|
||||
s.reportErr(UnexpectedOfflineError(o.Clone()))
|
||||
s.reportErr(e.NewError(EUnexpectedOffline, o))
|
||||
}
|
||||
o.Offline = false
|
||||
s.dispatchIter(o)
|
||||
@@ -372,14 +390,14 @@ func (s *State) processEvent(e *Event) {
|
||||
case uevent.KOBJ_OFFLINE:
|
||||
o, ok := s.uevent[e.DevPath]
|
||||
if !ok {
|
||||
s.reportErr(TargetError(e.Clone()))
|
||||
s.reportErr(e.NewError(EBadTarget, nil))
|
||||
// coldboot race window similar to an unexpected KOBJ_MOVE
|
||||
o = e.makeColdboot()
|
||||
s.uevent[e.DevPath] = o
|
||||
o.merge(e.Env)
|
||||
}
|
||||
if o.Offline {
|
||||
s.reportErr(UnexpectedOfflineError(o.Clone()))
|
||||
s.reportErr(e.NewError(EUnexpectedOffline, o))
|
||||
}
|
||||
o.Offline = true
|
||||
s.dispatchIter(o)
|
||||
@@ -388,13 +406,13 @@ func (s *State) processEvent(e *Event) {
|
||||
case uevent.KOBJ_BIND:
|
||||
o, ok := s.uevent[e.DevPath]
|
||||
if !ok {
|
||||
s.reportErr(TargetError(e.Clone()))
|
||||
s.reportErr(e.NewError(EBadTarget, nil))
|
||||
// coldboot race window similar to an unexpected KOBJ_MOVE
|
||||
o = e.makeColdboot()
|
||||
s.uevent[e.DevPath] = o
|
||||
}
|
||||
if o.State != StateColdboot && o.State != StateNew {
|
||||
s.reportErr(BindStateError(o.Clone()))
|
||||
s.reportErr(e.NewError(EBindState, o))
|
||||
}
|
||||
o.State = StateBound
|
||||
o.merge(e.Env)
|
||||
@@ -404,13 +422,13 @@ func (s *State) processEvent(e *Event) {
|
||||
case uevent.KOBJ_UNBIND:
|
||||
o, ok := s.uevent[e.DevPath]
|
||||
if !ok {
|
||||
s.reportErr(TargetError(e.Clone()))
|
||||
s.reportErr(e.NewError(EBadTarget, nil))
|
||||
// coldboot race window similar to an unexpected KOBJ_MOVE, but does
|
||||
// not result in inconsistent state if dropped
|
||||
return
|
||||
}
|
||||
if o.State != StateBound {
|
||||
s.reportErr(UnbindStateError(o.Clone()))
|
||||
s.reportErr(e.NewError(EUnbindState, o))
|
||||
}
|
||||
o.State = StateNew
|
||||
o.Driver = ""
|
||||
@@ -423,7 +441,7 @@ func (s *State) processEvent(e *Event) {
|
||||
}
|
||||
}
|
||||
|
||||
// BadSequenceError is reported by [State.Consume] for an unexpected SEQNUM.
|
||||
// BadSequenceError is reported for an unexpected SEQNUM.
|
||||
type BadSequenceError struct{ Got, Want uint64 }
|
||||
|
||||
func (e BadSequenceError) Error() string {
|
||||
|
||||
Reference in New Issue
Block a user