Skip to content
This repository has been archived by the owner on Jul 28, 2021. It is now read-only.

Commit

Permalink
Support responses with both error messages and stack traces
Browse files Browse the repository at this point in the history
The HCS now can accept stack trace information in the ErrorRecord
struct. As a result, the GCS should provide that information back on
error.
  • Loading branch information
beweedon committed Aug 7, 2017
1 parent 7f54f58 commit 891e7f7
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 5 deletions.
9 changes: 5 additions & 4 deletions service/gcs/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,13 +407,13 @@ func newResponseBase() *prot.MessageResponseBase {
// contain information pertaining to the given error.
func (b *bridge) setErrorForResponseBase(response *prot.MessageResponseBase, errForResponse error) {
errorMessage := errForResponse.Error()
stackString := ""
fileName := ""
lineNumber := -1
functionName := ""
if serr, ok := errForResponse.(gcserr.StackTracer); ok {
frames := serr.StackTrace()
bottomFrame := frames[0]
errorMessage = fmt.Sprintf("%+v", serr)
if stack := gcserr.BaseStackTrace(errForResponse); stack != nil {
bottomFrame := stack[0]
stackString = fmt.Sprintf("%+v", stack)
fileName = fmt.Sprintf("%s", bottomFrame)
lineNumberStr := fmt.Sprintf("%d", bottomFrame)
var err error
Expand All @@ -433,6 +433,7 @@ func (b *bridge) setErrorForResponseBase(response *prot.MessageResponseBase, err
newRecord := prot.ErrorRecord{
Result: int32(hresult),
Message: errorMessage,
StackTrace: stackString,
ModuleName: "gcs",
FileName: fileName,
Line: uint32(lineNumber),
Expand Down
27 changes: 27 additions & 0 deletions service/gcs/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,33 @@ type StackTracer interface {
StackTrace() errors.StackTrace
}

// BaseStackTrace gets the earliest errors.StackTrace in the given error's
// cause stack. This will be the stack trace which reaches closest to the
// error's actual origin. It returns nil if no stack trace is found in the
// cause stack.
func BaseStackTrace(e error) errors.StackTrace {
type causer interface {
Cause() error
}
cause := e
var tracer StackTracer
for cause != nil {
serr, ok := cause.(StackTracer)
if ok {
tracer = serr
}
cerr, ok := cause.(causer)
if !ok {
break
}
cause = cerr.Cause()
}
if tracer == nil {
return nil
}
return tracer.StackTrace()
}

type baseHresultError struct {
hresult Hresult
}
Expand Down
51 changes: 51 additions & 0 deletions service/gcs/errors/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,57 @@ type stackTraceCauseError interface {

var _ = Describe("Errors", func() {
Describe("unittests", func() {
Describe("getting the base stack trace", func() {
Context("only one error in cause stack", func() {
var (
e error
stackTrace errors.StackTrace
)
BeforeEach(func() {
e = errors.New("test")
stackTrace = BaseStackTrace(e)
})
It("should return the same stack trace as the single error", func() {
se, ok := e.(StackTracer)
Expect(ok).To(BeTrue())
Expect(stackTrace).To(Equal(se.StackTrace()))
})
})
Context("multiple StackTracers in cause stack", func() {
var (
e1 error
e2 error
e3 error
e4 error
stackTrace errors.StackTrace
)
BeforeEach(func() {
e1 = fmt.Errorf("test")
e2 = errors.WithStack(e1)
e3 = errors.WithStack(e2)
e4 = errors.WithMessage(e3, "test2")
stackTrace = BaseStackTrace(e4)
})
It("should return the same stack trace as the bottom-most StackTracer", func() {
se, ok := e2.(StackTracer)
Expect(ok).To(BeTrue())
Expect(stackTrace).To(Equal(se.StackTrace()))
})
})
Context("no StackTracers in cause stack", func() {
var (
e error
stackTrace errors.StackTrace
)
BeforeEach(func() {
e = fmt.Errorf("test")
stackTrace = BaseStackTrace(e)
})
It("should return nil", func() {
Expect(stackTrace).To(BeNil())
})
})
})
Describe("constructing HRESULT errors", func() {
Context("with no wrapped error", func() {
var (
Expand Down
3 changes: 2 additions & 1 deletion service/gcs/prot/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,10 +335,11 @@ func UnmarshalContainerModifySettings(b []byte) (*ContainerModifySettings, error

// ErrorRecord represents a single error to be reported back to the HCS. It
// allows for specifying information about the source of the error, as well as
// an error message.
// an error message and stack trace.
type ErrorRecord struct {
Result int32
Message string
StackTrace string `json:",omitempty"`
ModuleName string
FileName string
Line uint32
Expand Down

0 comments on commit 891e7f7

Please sign in to comment.