Merge "Expand errors that can be returned from RPCs" into studio-master-dev
diff --git a/builder/follow.go b/builder/follow.go
index 888ef00..13cd218 100644
--- a/builder/follow.go
+++ b/builder/follow.go
@@ -29,20 +29,20 @@
if err != nil {
return nil, err
}
- if linker, ok := obj.(path.Linker); ok {
- link, err := linker.Link(ctx, r.Path, d)
- if err != nil {
- return link, fmt.Errorf("Following path %v gave an error", r.Path, err)
- }
- if link == nil {
- return nil, fmt.Errorf("Following path %v gave a nil link", r.Path)
- }
- err = link.Validate()
- if err != nil {
- return nil, fmt.Errorf("Following path %v gave an invalid link %v: %v",
- r.Path, link, err)
- }
+ linker, ok := obj.(path.Linker)
+ if !ok {
+ return nil, &path.ErrNotFollowable{Path: r.Path}
+ }
+ link, err := linker.Link(ctx, r.Path, d)
+ if err != nil {
return link, err
}
- return nil, fmt.Errorf("Path %v can not be followed", r.Path)
+ if link == nil {
+ return nil, fmt.Errorf("Following path %v gave a nil link", r.Path)
+ }
+ if err := link.Validate(); err != nil {
+ return nil, fmt.Errorf("Following path %v gave an invalid link %v: %v",
+ r.Path, link, err)
+ }
+ return link, nil
}
diff --git a/gfxapi/gles/links.go b/gfxapi/gles/links.go
index 48fb889..5ed748d 100644
--- a/gfxapi/gles/links.go
+++ b/gfxapi/gles/links.go
@@ -15,8 +15,6 @@
package gles
import (
- "fmt"
-
"android.googlesource.com/platform/tools/gpu/atom"
"android.googlesource.com/platform/tools/gpu/builder"
"android.googlesource.com/platform/tools/gpu/database"
@@ -38,7 +36,7 @@
MapIndex(uint64(state.CurrentThread)).
Field("Instances"), nil
}
- return nil, fmt.Errorf("Path %v is not a path to an atom", p)
+ return nil, &path.ErrNotFollowable{Path: p}
}
func (o AttributeLocation) Link(ctx log.Context, p path.Path, d database.Database) (path.Path, error) {
@@ -136,7 +134,7 @@
Field("Uniforms").
MapIndex(int64(o)), nil
} else {
- return nil, fmt.Errorf("Path %v is not a path to an atom", p)
+ return nil, &path.ErrNotFollowable{Path: p}
}
}
diff --git a/rpc/client.go b/rpc/client.go
index cda8773..1126948 100644
--- a/rpc/client.go
+++ b/rpc/client.go
@@ -72,7 +72,7 @@
}
// Write the RPC header
- if e.Data(header[:]); d.Error() != nil {
+ if e.Data(headerV1[:]); d.Error() != nil {
return nil, d.Error()
}
@@ -93,7 +93,7 @@
}
// Check to see if the response was an error
- if err, b := res.(*Error); b {
+ if err, b := res.(Err); b {
return nil, err
}
diff --git a/rpc/error.go b/rpc/error.go
new file mode 100644
index 0000000..341abab
--- /dev/null
+++ b/rpc/error.go
@@ -0,0 +1,78 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// 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 rpc
+
+import (
+ "fmt"
+
+ "android.googlesource.com/platform/tools/gpu/binary"
+)
+
+// Err is the interface implemented by errors that can be sent over the wire.
+type Err interface {
+ binary.Object
+ Error() string
+}
+
+// Error is an implementation of Err that holds a single string error message.
+type Error struct {
+ binary.Generate `implements:"rpc.Err" java:"RpcError"`
+ Msg string
+}
+
+func (e *Error) Error() string { return e.Msg }
+
+// ErrInvalidHeader is the error returned when the RPC call begins with an
+// invalid header code.
+type ErrInvalidHeader struct {
+ binary.Generate `implements:"rpc.Err"`
+ Header [4]byte
+}
+
+func (e ErrInvalidHeader) Error() string {
+ return fmt.Sprintf("Invalid RPC header: %v", e.Header)
+}
+
+// ErrDecodingCall is the error returned when a call cannot be decoded by the
+// server.
+type ErrDecodingCall struct {
+ binary.Generate `implements:"rpc.Err"`
+ Reason string
+}
+
+func (e ErrDecodingCall) Error() string {
+ return fmt.Sprintf("Error decoding RPC call: %v", e.Reason)
+}
+
+// ErrUnknownFunction is the error returned when a call is made to a function
+// unrecognised by the server.
+type ErrUnknownFunction struct {
+ binary.Generate `implements:"rpc.Err"`
+ Function string
+}
+
+func (e ErrUnknownFunction) Error() string {
+ return fmt.Sprintf("Unknown RPC function: %v", e.Function)
+}
+
+// ErrPanic is the error returned when an RPC call raises an uncaught panic.
+type ErrPanic struct {
+ binary.Generate `implements:"rpc.Err"`
+ Msg string
+}
+
+func (e ErrPanic) Error() string {
+ return fmt.Sprintf("RPC panic: %v", e.Msg)
+}
diff --git a/rpc/rpc.go b/rpc/rpc.go
index 708df67..0161715 100644
--- a/rpc/rpc.go
+++ b/rpc/rpc.go
@@ -23,27 +23,7 @@
// binary: java.indent = " "
// binary: java.member_prefix = m
-import (
- "fmt"
-
- "android.googlesource.com/platform/tools/gpu/binary"
+var (
+ headerV0 = [4]byte{'r', 'p', 'c', '0'}
+ headerV1 = [4]byte{'r', 'p', 'c', '1'}
)
-
-var header = [4]byte{'r', 'p', 'c', '0'}
-
-// ErrInvalidHeader is returned when either client or server detects an
-// incorrectly formed rpc header.
-var ErrInvalidHeader = NewError("Invalid RPC header")
-
-// NewError is used to create new rpc error objects with the specified human readable message.
-func NewError(msg string, args ...interface{}) *Error {
- return &Error{message: fmt.Sprintf(msg, args...)}
-}
-
-// Error is an implementation of error that can be sent over the wire.
-type Error struct {
- binary.Generate `java:"RpcError"`
- message string
-}
-
-func (e *Error) Error() string { return e.message }
diff --git a/rpc/rpc_binary.go b/rpc/rpc_binary.go
index 5ba8b97..f24ba0c 100644
--- a/rpc/rpc_binary.go
+++ b/rpc/rpc_binary.go
@@ -14,28 +14,161 @@
import _ "android.googlesource.com/platform/tools/gpu/binary/schema"
const (
- ixǁError = iota
+ ixǁErrDecodingCall = iota
+ ixǁErrInvalidHeader
+ ixǁErrPanic
+ ixǁErrUnknownFunction
+ ixǁError
)
-var entities [1]binary.Entity
+var entities [5]binary.Entity
var Namespace = registry.NewNamespace()
func init() {
registry.Global.AddFallbacks(Namespace)
+ Namespace.AddClassOf((*ErrDecodingCall)(nil))
+ Namespace.AddClassOf((*ErrInvalidHeader)(nil))
+ Namespace.AddClassOf((*ErrPanic)(nil))
+ Namespace.AddClassOf((*ErrUnknownFunction)(nil))
Namespace.AddClassOf((*Error)(nil))
}
+var _ Err = (*ErrDecodingCall)(nil) // Interface compliance check.
+type binaryClassErrDecodingCall struct{}
+
+func (*ErrDecodingCall) Class() binary.Class {
+ return (*binaryClassErrDecodingCall)(nil)
+}
+func doEncodeErrDecodingCall(e binary.Writer, o *ErrDecodingCall) {
+ e.String(o.Reason)
+}
+func doDecodeErrDecodingCall(d binary.Reader, o *ErrDecodingCall) {
+ o.Reason = string(d.String())
+}
+func (*binaryClassErrDecodingCall) Encode(e binary.Encoder, obj binary.Object) {
+ doEncodeErrDecodingCall(e, obj.(*ErrDecodingCall))
+}
+func (*binaryClassErrDecodingCall) New() binary.Object {
+ return &ErrDecodingCall{}
+}
+func (*binaryClassErrDecodingCall) DecodeTo(d binary.Decoder, obj binary.Object) {
+ doDecodeErrDecodingCall(d, obj.(*ErrDecodingCall))
+}
+func (o *ErrDecodingCall) WriteSimple(w binary.Writer) {
+ doEncodeErrDecodingCall(w, o)
+}
+func (o *ErrDecodingCall) ReadSimple(r binary.Reader) {
+ doDecodeErrDecodingCall(r, o)
+}
+func (c *binaryClassErrDecodingCall) Schema() *binary.Entity {
+ return &entities[ixǁErrDecodingCall]
+}
+
+var _ Err = (*ErrInvalidHeader)(nil) // Interface compliance check.
+type binaryClassErrInvalidHeader struct{}
+
+func (*ErrInvalidHeader) Class() binary.Class {
+ return (*binaryClassErrInvalidHeader)(nil)
+}
+func doEncodeErrInvalidHeader(e binary.Writer, o *ErrInvalidHeader) {
+ e.Data(o.Header[:4])
+}
+func doDecodeErrInvalidHeader(d binary.Reader, o *ErrInvalidHeader) {
+ d.Data(o.Header[:4])
+}
+func (*binaryClassErrInvalidHeader) Encode(e binary.Encoder, obj binary.Object) {
+ doEncodeErrInvalidHeader(e, obj.(*ErrInvalidHeader))
+}
+func (*binaryClassErrInvalidHeader) New() binary.Object {
+ return &ErrInvalidHeader{}
+}
+func (*binaryClassErrInvalidHeader) DecodeTo(d binary.Decoder, obj binary.Object) {
+ doDecodeErrInvalidHeader(d, obj.(*ErrInvalidHeader))
+}
+func (o *ErrInvalidHeader) WriteSimple(w binary.Writer) {
+ doEncodeErrInvalidHeader(w, o)
+}
+func (o *ErrInvalidHeader) ReadSimple(r binary.Reader) {
+ doDecodeErrInvalidHeader(r, o)
+}
+func (c *binaryClassErrInvalidHeader) Schema() *binary.Entity {
+ return &entities[ixǁErrInvalidHeader]
+}
+
+var _ Err = (*ErrPanic)(nil) // Interface compliance check.
+type binaryClassErrPanic struct{}
+
+func (*ErrPanic) Class() binary.Class {
+ return (*binaryClassErrPanic)(nil)
+}
+func doEncodeErrPanic(e binary.Writer, o *ErrPanic) {
+ e.String(o.Msg)
+}
+func doDecodeErrPanic(d binary.Reader, o *ErrPanic) {
+ o.Msg = string(d.String())
+}
+func (*binaryClassErrPanic) Encode(e binary.Encoder, obj binary.Object) {
+ doEncodeErrPanic(e, obj.(*ErrPanic))
+}
+func (*binaryClassErrPanic) New() binary.Object {
+ return &ErrPanic{}
+}
+func (*binaryClassErrPanic) DecodeTo(d binary.Decoder, obj binary.Object) {
+ doDecodeErrPanic(d, obj.(*ErrPanic))
+}
+func (o *ErrPanic) WriteSimple(w binary.Writer) {
+ doEncodeErrPanic(w, o)
+}
+func (o *ErrPanic) ReadSimple(r binary.Reader) {
+ doDecodeErrPanic(r, o)
+}
+func (c *binaryClassErrPanic) Schema() *binary.Entity {
+ return &entities[ixǁErrPanic]
+}
+
+var _ Err = (*ErrUnknownFunction)(nil) // Interface compliance check.
+type binaryClassErrUnknownFunction struct{}
+
+func (*ErrUnknownFunction) Class() binary.Class {
+ return (*binaryClassErrUnknownFunction)(nil)
+}
+func doEncodeErrUnknownFunction(e binary.Writer, o *ErrUnknownFunction) {
+ e.String(o.Function)
+}
+func doDecodeErrUnknownFunction(d binary.Reader, o *ErrUnknownFunction) {
+ o.Function = string(d.String())
+}
+func (*binaryClassErrUnknownFunction) Encode(e binary.Encoder, obj binary.Object) {
+ doEncodeErrUnknownFunction(e, obj.(*ErrUnknownFunction))
+}
+func (*binaryClassErrUnknownFunction) New() binary.Object {
+ return &ErrUnknownFunction{}
+}
+func (*binaryClassErrUnknownFunction) DecodeTo(d binary.Decoder, obj binary.Object) {
+ doDecodeErrUnknownFunction(d, obj.(*ErrUnknownFunction))
+}
+func (o *ErrUnknownFunction) WriteSimple(w binary.Writer) {
+ doEncodeErrUnknownFunction(w, o)
+}
+func (o *ErrUnknownFunction) ReadSimple(r binary.Reader) {
+ doDecodeErrUnknownFunction(r, o)
+}
+func (c *binaryClassErrUnknownFunction) Schema() *binary.Entity {
+ return &entities[ixǁErrUnknownFunction]
+}
+
+var _ Err = (*Error)(nil) // Interface compliance check.
type binaryClassError struct{}
func (*Error) Class() binary.Class {
return (*binaryClassError)(nil)
}
func doEncodeError(e binary.Writer, o *Error) {
- e.String(o.message)
+ e.String(o.Msg)
}
func doDecodeError(d binary.Reader, o *Error) {
- o.message = string(d.String())
+ o.Msg = string(d.String())
}
func (*binaryClassError) Encode(e binary.Encoder, obj binary.Object) {
doEncodeError(e, obj.(*Error))
diff --git a/rpc/rpc_binary_test.go b/rpc/rpc_binary_test.go
index a454fc4..6c81520 100644
--- a/rpc/rpc_binary_test.go
+++ b/rpc/rpc_binary_test.go
@@ -12,14 +12,16 @@
const (
ixǁdelay = iota
+ ixǁerr
ixǁrequest
ixǁresponse
)
-var test_entities [3]binary.Entity
+var test_entities [4]binary.Entity
func init() {
Namespace.AddClassOf((*delay)(nil))
+ Namespace.AddClassOf((*err)(nil))
Namespace.AddClassOf((*request)(nil))
Namespace.AddClassOf((*response)(nil))
}
@@ -54,6 +56,37 @@
return &test_entities[ixǁdelay]
}
+var _ Err = (*err)(nil) // Interface compliance check.
+type binaryClasserr struct{}
+
+func (*err) Class() binary.Class {
+ return (*binaryClasserr)(nil)
+}
+func doEncodeerr(e binary.Writer, o *err) {
+ e.String(o.msg)
+}
+func doDecodeerr(d binary.Reader, o *err) {
+ o.msg = string(d.String())
+}
+func (*binaryClasserr) Encode(e binary.Encoder, obj binary.Object) {
+ doEncodeerr(e, obj.(*err))
+}
+func (*binaryClasserr) New() binary.Object {
+ return &err{}
+}
+func (*binaryClasserr) DecodeTo(d binary.Decoder, obj binary.Object) {
+ doDecodeerr(d, obj.(*err))
+}
+func (o *err) WriteSimple(w binary.Writer) {
+ doEncodeerr(w, o)
+}
+func (o *err) ReadSimple(r binary.Reader) {
+ doDecodeerr(r, o)
+}
+func (c *binaryClasserr) Schema() *binary.Entity {
+ return &test_entities[ixǁerr]
+}
+
type binaryClassrequest struct{}
func (*request) Class() binary.Class {
diff --git a/rpc/rpc_test.go b/rpc/rpc_test.go
index 7415e59..a14a9ba 100644
--- a/rpc/rpc_test.go
+++ b/rpc/rpc_test.go
@@ -15,7 +15,9 @@
package rpc
import (
+ "fmt"
"io"
+ "reflect"
"testing"
"android.googlesource.com/platform/tools/gpu/binary"
@@ -40,20 +42,27 @@
data string
}
+type err struct {
+ binary.Generate `implements:"rpc.Err"`
+ msg string
+}
+
+func (e err) Error() string { return e.msg }
+
func create(t *testing.T) Client {
ctx := log.Testing(t)
pass := make(chan string, 1)
sr, cw := io.Pipe()
cr, sw := io.Pipe()
- Serve(ctx, sr, sw, sw, mtu, func(ctx log.Context, call interface{}) binary.Object {
+ Serve(ctx, sr, sw, sw, mtu, func(ctx log.Context, call interface{}) (binary.Object, error) {
switch o := call.(type) {
case *request:
pass <- o.data
- return &response{data: o.data}
+ return &response{data: o.data}, nil
case *delay:
- return &response{data: <-pass}
+ return &response{data: <-pass}, nil
default:
- return NewError("Invalid call type %T", o)
+ return nil, &err{msg: fmt.Sprintf("Invalid call type %T", o)}
}
})
return NewClient(multiplexer.New(ctx, cr, cw, cw, mtu, nil), nil)
@@ -88,6 +97,18 @@
simpleRequest(t, c, "hello")
}
+func TestError(t *testing.T) {
+ c := create(t)
+ data, got := c.Send(&err{msg: "not an RPC method"})
+ if data != nil {
+ t.Fatalf("Expected no data. Got: %v", data)
+ }
+ expected := &err{msg: "Invalid call type *rpc.err"}
+ if !reflect.DeepEqual(got, expected) {
+ t.Fatalf("Unexpected error from rpc. Expected: %v, got: %v", expected, got)
+ }
+}
+
func TestInterleavedRpc(t *testing.T) {
c := create(t)
done := make(chan struct{})
diff --git a/rpc/server.go b/rpc/server.go
index 7afb6bf..c4102e4 100644
--- a/rpc/server.go
+++ b/rpc/server.go
@@ -29,7 +29,7 @@
)
// Handler is the signature for a function that handles incoming rpc calls.
-type Handler func(log.Context, interface{}) binary.Object
+type Handler func(log.Context, interface{}) (binary.Object, error)
var lastTaskID = uint32(1)
@@ -51,30 +51,50 @@
d := cyclic.Decoder(vle.Reader(channel))
e := cyclic.Encoder(vle.Writer(w))
+ // Function used for encoding an error
+ encErr := func(err error) {
+ if rpcErr, ok := err.(Err); ok {
+ e.Object(rpcErr)
+ } else {
+ e.Object(&Error{Msg: err.Error()})
+ }
+ }
+
// Check the RPC header
var h [4]byte
- if d.Data(h[:]); d.Error() != nil || h != header {
- ctx.Fail(ErrInvalidHeader, "")
- e.Object(ErrInvalidHeader)
- return
+ d.Data(h[:])
+
+ switch h {
+ case headerV0:
+ // Legacy support for a single string error message type.
+ encErr = func(err error) { e.Object(&Error{Msg: err.Error()}) }
+ case headerV1:
+ default:
+ err := &ErrInvalidHeader{Header: h}
+ ctx.Fail(err, "")
+ encErr(err)
}
// Decode the call
val := d.Object()
- if d.Error() != nil {
- ctx.Error().Fail(d.Error(), "Decoding call")
- e.Object(NewError("Failed to decode call. Reason: %v", d.Error()))
+ if err := d.Error(); err != nil {
+ ctx.Error().Fail(err, "Decoding call")
+ encErr(&ErrDecodingCall{Reason: err.Error()})
return
}
// Invoke the call
- res := handler(ctx, val)
+ res, err := handler(ctx, val)
// Encode the call result
- e.Object(res)
+ if err == nil {
+ e.Object(res) // Success
+ } else {
+ encErr(err) // Failure
+ }
+
if err := e.Error(); err != nil {
ctx.Error().V("value", val).Fail(err, "Encoding result")
- e.Object(NewError("Failed to encode call result. Reason: %v", err))
return
}
})
diff --git a/rpc/test/rpc_test_server.go b/rpc/test/rpc_test_server.go
index 8953d3a..b53faca 100644
--- a/rpc/test/rpc_test_server.go
+++ b/rpc/test/rpc_test_server.go
@@ -21,83 +21,84 @@
}
func BindServer(ctx log.Context, r io.Reader, w io.Writer, c io.Closer, mtu int, server Server) {
- rpc.Serve(ctx, r, w, c, mtu, func(ctx log.Context, in interface{}) (res binary.Object) {
- ctx = ctx.Enter(fmt.Sprintf("%T", in))
+ rpc.Serve(ctx, r, w, c, mtu, func(ctx log.Context, in interface{}) (res binary.Object, err error) {
+ ty := fmt.Sprintf("%T", in)
+ ctx = ctx.Enter(ty)
defer func() {
if r := recover(); r == nil {
ctx.Info().Tag("rpc").WithValue("result", res).Log("")
} else {
- err, ok := r.(error)
- if !ok {
- err = fmt.Errorf("%v", err)
+ var ok bool
+ if err, ok = r.(error); !ok {
+ err = fmt.Errorf("%v", r)
}
ctx.Fail(err, "")
- res = rpc.NewError(fmt.Sprintf("Panic: '%v'. See gapis log for more information.", err))
+ err = &rpc.ErrPanic{Msg: err.Error()}
}
}()
switch call := in.(type) {
case *callAdd:
if res, err := server.Add(ctx, call.a, call.b); err == nil {
- return &resultAdd{value: res}
+ return &resultAdd{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callEnumToString:
if res, err := server.EnumToString(ctx, call.e); err == nil {
- return &resultEnumToString{value: res}
+ return &resultEnumToString{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetListNodeChain:
if res, err := server.GetListNodeChain(ctx); err == nil {
- return &resultGetListNodeChain{value: res}
+ return &resultGetListNodeChain{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetListNodeChainArray:
if res, err := server.GetListNodeChainArray(ctx); err == nil {
- return &resultGetListNodeChainArray{value: res}
+ return &resultGetListNodeChainArray{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetResource:
if res, err := server.GetResource(ctx); err == nil {
- return &resultGetResource{value: res}
+ return &resultGetResource{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetSingleListNode:
if res, err := server.GetSingleListNode(ctx); err == nil {
- return &resultGetSingleListNode{value: res}
+ return &resultGetSingleListNode{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetStruct:
if res, err := server.GetStruct(ctx); err == nil {
- return &resultGetStruct{value: res}
+ return &resultGetStruct{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callResolveResource:
if res, err := server.ResolveResource(ctx, call.r); err == nil {
- return &resultResolveResource{value: res}
+ return &resultResolveResource{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callSetStruct:
if err := server.SetStruct(ctx, call.s); err == nil {
- return &resultSetStruct{}
+ return &resultSetStruct{}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callUseResource:
if err := server.UseResource(ctx, call.r); err == nil {
- return &resultUseResource{}
+ return &resultUseResource{}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
default:
- return rpc.NewError("Unexpected function: %T", call)
+ return nil, &rpc.ErrUnknownFunction{Function: ty}
}
})
}
diff --git a/service/errors.go b/service/errors.go
new file mode 100644
index 0000000..06fbf3c
--- /dev/null
+++ b/service/errors.go
@@ -0,0 +1,15 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// 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 service
diff --git a/service/path/errors.go b/service/path/errors.go
new file mode 100644
index 0000000..b6e9928
--- /dev/null
+++ b/service/path/errors.go
@@ -0,0 +1,32 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// 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 path
+
+import (
+ "fmt"
+
+ "android.googlesource.com/platform/tools/gpu/binary"
+)
+
+// ErrNotFollowable is the error returned when a path cannot be followed
+// using the Service.Follow RPC function.
+type ErrNotFollowable struct {
+ binary.Generate `implements:"rpc.Err" java:"ErrPathNotFollowable"`
+ Path Path
+}
+
+func (e *ErrNotFollowable) Error() string {
+ return fmt.Sprintf("Path '%v' is not followable", e.Path)
+}
diff --git a/service/path/path_binary.go b/service/path/path_binary.go
index 7022dd4..1d79d0a 100644
--- a/service/path/path_binary.go
+++ b/service/path/path_binary.go
@@ -9,6 +9,7 @@
"android.googlesource.com/platform/tools/gpu/binary"
"android.googlesource.com/platform/tools/gpu/binary/registry"
"android.googlesource.com/platform/tools/gpu/binary/schema"
+ "android.googlesource.com/platform/tools/gpu/rpc"
)
// Make sure schema init() runs first
@@ -22,6 +23,7 @@
ixǁAtom
ixǁBlob
ixǁDevice
+ ixǁErrNotFollowable
ixǁField
ixǁHierarchy
ixǁImageInfo
@@ -36,7 +38,7 @@
ixǁTimingInfo
)
-var entities [19]binary.Entity
+var entities [20]binary.Entity
var Namespace = registry.NewNamespace()
@@ -49,6 +51,7 @@
Namespace.AddClassOf((*Atom)(nil))
Namespace.AddClassOf((*Blob)(nil))
Namespace.AddClassOf((*Device)(nil))
+ Namespace.AddClassOf((*ErrNotFollowable)(nil))
Namespace.AddClassOf((*Field)(nil))
Namespace.AddClassOf((*Hierarchy)(nil))
Namespace.AddClassOf((*ImageInfo)(nil))
@@ -271,6 +274,31 @@
return &entities[ixǁDevice]
}
+var _ rpc.Err = (*ErrNotFollowable)(nil) // Interface compliance check.
+type binaryClassErrNotFollowable struct{}
+
+func (*ErrNotFollowable) Class() binary.Class {
+ return (*binaryClassErrNotFollowable)(nil)
+}
+func doEncodeErrNotFollowable(e binary.Encoder, o *ErrNotFollowable) {
+ e.Object(o.Path)
+}
+func doDecodeErrNotFollowable(d binary.Decoder, o *ErrNotFollowable) {
+ o.Path = PathCast(d.Object())
+}
+func (*binaryClassErrNotFollowable) Encode(e binary.Encoder, obj binary.Object) {
+ doEncodeErrNotFollowable(e, obj.(*ErrNotFollowable))
+}
+func (*binaryClassErrNotFollowable) New() binary.Object {
+ return &ErrNotFollowable{}
+}
+func (*binaryClassErrNotFollowable) DecodeTo(d binary.Decoder, obj binary.Object) {
+ doDecodeErrNotFollowable(d, obj.(*ErrNotFollowable))
+}
+func (c *binaryClassErrNotFollowable) Schema() *binary.Entity {
+ return &entities[ixǁErrNotFollowable]
+}
+
type binaryClassField struct{}
func (*Field) Class() binary.Class {
diff --git a/service/service_server.go b/service/service_server.go
index cb2c782..a2696b8 100644
--- a/service/service_server.go
+++ b/service/service_server.go
@@ -22,107 +22,108 @@
}
func BindServer(ctx log.Context, r io.Reader, w io.Writer, c io.Closer, mtu int, server Server) {
- rpc.Serve(ctx, r, w, c, mtu, func(ctx log.Context, in interface{}) (res binary.Object) {
- ctx = ctx.Enter(fmt.Sprintf("%T", in))
+ rpc.Serve(ctx, r, w, c, mtu, func(ctx log.Context, in interface{}) (res binary.Object, err error) {
+ ty := fmt.Sprintf("%T", in)
+ ctx = ctx.Enter(ty)
defer func() {
if r := recover(); r == nil {
ctx.Info().Tag("rpc").WithValue("result", res).Log("")
} else {
- err, ok := r.(error)
- if !ok {
- err = fmt.Errorf("%v", err)
+ var ok bool
+ if err, ok = r.(error); !ok {
+ err = fmt.Errorf("%v", r)
}
ctx.Fail(err, "")
- res = rpc.NewError(fmt.Sprintf("Panic: '%v'. See gapis log for more information.", err))
+ err = &rpc.ErrPanic{Msg: err.Error()}
}
}()
switch call := in.(type) {
case *callBeginCPUProfile:
if err := server.BeginCPUProfile(ctx); err == nil {
- return &resultBeginCPUProfile{}
+ return &resultBeginCPUProfile{}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callEndCPUProfile:
if res, err := server.EndCPUProfile(ctx); err == nil {
- return &resultEndCPUProfile{value: res}
+ return &resultEndCPUProfile{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callFollow:
if res, err := server.Follow(ctx, call.p); err == nil {
- return &resultFollow{value: res}
+ return &resultFollow{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGet:
if res, err := server.Get(ctx, call.p); err == nil {
- return &resultGet{value: res}
+ return &resultGet{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetCaptures:
if res, err := server.GetCaptures(ctx); err == nil {
- return &resultGetCaptures{value: res}
+ return &resultGetCaptures{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetDevices:
if res, err := server.GetDevices(ctx); err == nil {
- return &resultGetDevices{value: res}
+ return &resultGetDevices{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetFeatures:
if res, err := server.GetFeatures(ctx); err == nil {
- return &resultGetFeatures{value: res}
+ return &resultGetFeatures{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetFramebufferColor:
if res, err := server.GetFramebufferColor(ctx, call.device, call.after, call.settings); err == nil {
- return &resultGetFramebufferColor{value: res}
+ return &resultGetFramebufferColor{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetFramebufferDepth:
if res, err := server.GetFramebufferDepth(ctx, call.device, call.after); err == nil {
- return &resultGetFramebufferDepth{value: res}
+ return &resultGetFramebufferDepth{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetSchema:
if res, err := server.GetSchema(ctx); err == nil {
- return &resultGetSchema{value: res}
+ return &resultGetSchema{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callGetTimingInfo:
if res, err := server.GetTimingInfo(ctx, call.device, call.capture, call.flags); err == nil {
- return &resultGetTimingInfo{value: res}
+ return &resultGetTimingInfo{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callImportCapture:
if res, err := server.ImportCapture(ctx, call.name, call.Data); err == nil {
- return &resultImportCapture{value: res}
+ return &resultImportCapture{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callLoadCapture:
if res, err := server.LoadCapture(ctx, call.path); err == nil {
- return &resultLoadCapture{value: res}
+ return &resultLoadCapture{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
case *callSet:
if res, err := server.Set(ctx, call.p, call.v); err == nil {
- return &resultSet{value: res}
+ return &resultSet{value: res}, nil
} else {
- return rpc.NewError(err.Error())
+ return nil, err
}
default:
- return rpc.NewError("Unexpected function: %T", call)
+ return nil, &rpc.ErrUnknownFunction{Function: ty}
}
})
}
diff --git a/signatures.txt b/signatures.txt
index bc8ad50..2acb4da 100644
--- a/signatures.txt
+++ b/signatures.txt
@@ -3788,6 +3788,9 @@
Device : pod : full 39 compact 19
path.Device{[20]Uint8}
+ErrNotFollowable : entity : full 39 compact 27
+path.ErrNotFollowable{?}
+
Field : entity : full 43 compact 17
path.Field{?,String}
@@ -3830,7 +3833,19 @@
ResourceInfo : pod : full 52 compact 28
protocol.ResourceInfo{String,Uint32}
-Error : pod : full 32 compact 15
+ErrDecodingCall : pod : full 41 compact 25
+rpc.ErrDecodingCall{String}
+
+ErrInvalidHeader : pod : full 43 compact 28
+rpc.ErrInvalidHeader{[4]Uint8}
+
+ErrPanic : pod : full 31 compact 18
+rpc.ErrPanic{String}
+
+ErrUnknownFunction : pod : full 46 compact 28
+rpc.ErrUnknownFunction{String}
+
+Error : pod : full 28 compact 15
rpc.Error{String}
AtomRangeTimer : pod : full 91 compact 30
@@ -4434,12 +4449,12 @@
test.resultUseResource{}
Schema stats:
-Count: 1478
-Full: 153054
-Total: 152157
+Count: 1483
+Full: 153250
+Total: 152353
Average: 102
Largest: 4837
-Compact: 47062
-Total: 46172
+Compact: 47188
+Total: 46298
Average: 31
Largest: 174
diff --git a/tools/codergen/template/embed.go b/tools/codergen/template/embed.go
index ba8cd75..35fc16c 100644
--- a/tools/codergen/template/embed.go
+++ b/tools/codergen/template/embed.go
@@ -1029,18 +1029,19 @@
«}¶
¶
func BindServer(ctx log.Context, r io.Reader, w io.Writer, c io.Closer, mtu int, server Server) {Ȧ
- rpc.Serve(ctx, r, w, c, mtu, func(ctx log.Context, in interface{}) (res binary.Object) {Ȧ
- ctx = ctx.Enter(fmt.Sprintf("%T", in))¶
+ rpc.Serve(ctx, r, w, c, mtu, func(ctx log.Context, in interface{}) (res binary.Object, err error) {Ȧ
+ ty := fmt.Sprintf("%T", in)¶
+ ctx = ctx.Enter(ty)¶
defer func() {Ȧ
if r := recover(); r == nil {Ȧ
ctx.Info().Tag("rpc").WithValue("result", res).Log("")¶
«} else {»¶
- err, ok := r.(error)¶
- if !ok {Ȧ
- err = fmt.Errorf("%v", err)¶
+ var ok bool¶
+ if err, ok = r.(error); !ok {Ȧ
+ err = fmt.Errorf("%v", r)¶
«}¶
ctx.Fail(err, "")¶
- res = rpc.NewError(fmt.Sprintf("Panic: '%v'. See gapis log for more information.", err))¶
+ err = &rpc.ErrPanic{Msg: err.Error()}¶
«}¶
«}()¶
switch call := in.(type) {¶
@@ -1054,15 +1055,15 @@
); err == nil {Ȧ
return &{{.Result.Name}}{
{{if .Result.Type}}value: res{{end}}
- }¶
+ }, nil¶
«} else {»¶
- return rpc.NewError(err.Error())¶
+ return nil, err¶
«}¶
«
{{end}}
{{end}}
default:Ȧ
- return rpc.NewError("Unexpected function: %T", call)¶
+ return nil, &rpc.ErrUnknownFunction{Function: ty}¶
«
}¶
«})¶
diff --git a/tools/codergen/template/go_server.tmpl b/tools/codergen/template/go_server.tmpl
index 163aa65..c620ea7 100644
--- a/tools/codergen/template/go_server.tmpl
+++ b/tools/codergen/template/go_server.tmpl
@@ -29,18 +29,19 @@
«}¶
¶
func BindServer(ctx log.Context, r io.Reader, w io.Writer, c io.Closer, mtu int, server Server) {Ȧ
- rpc.Serve(ctx, r, w, c, mtu, func(ctx log.Context, in interface{}) (res binary.Object) {Ȧ
- ctx = ctx.Enter(fmt.Sprintf("%T", in))¶
+ rpc.Serve(ctx, r, w, c, mtu, func(ctx log.Context, in interface{}) (res binary.Object, err error) {Ȧ
+ ty := fmt.Sprintf("%T", in)¶
+ ctx = ctx.Enter(ty)¶
defer func() {Ȧ
if r := recover(); r == nil {Ȧ
ctx.Info().Tag("rpc").WithValue("result", res).Log("")¶
«} else {»¶
- err, ok := r.(error)¶
- if !ok {Ȧ
- err = fmt.Errorf("%v", err)¶
+ var ok bool¶
+ if err, ok = r.(error); !ok {Ȧ
+ err = fmt.Errorf("%v", r)¶
«}¶
ctx.Fail(err, "")¶
- res = rpc.NewError(fmt.Sprintf("Panic: '%v'. See gapis log for more information.", err))¶
+ err = &rpc.ErrPanic{Msg: err.Error()}¶
«}¶
«}()¶
switch call := in.(type) {¶
@@ -54,15 +55,15 @@
); err == nil {Ȧ
return &{{.Result.Name}}{
{{if .Result.Type}}value: res{{end}}
- }¶
+ }, nil¶
«} else {»¶
- return rpc.NewError(err.Error())¶
+ return nil, err¶
«}¶
«
{{end}}
{{end}}
default:Ȧ
- return rpc.NewError("Unexpected function: %T", call)¶
+ return nil, &rpc.ErrUnknownFunction{Function: ty}¶
«
}¶
«})¶