aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/cloud.google.com/go/cmd/go-cloud-debug-agent')
-rw-r--r--vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/debuglet.go450
-rw-r--r--vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints.go174
-rw-r--r--vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints_test.go168
-rw-r--r--vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client.go291
-rw-r--r--vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client_test.go254
-rw-r--r--vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector.go460
-rw-r--r--vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector_test.go418
7 files changed, 0 insertions, 2215 deletions
diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/debuglet.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/debuglet.go
deleted file mode 100644
index 6a8702c77..000000000
--- a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/debuglet.go
+++ /dev/null
@@ -1,450 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// 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.
-
-// +build linux
-
-package main
-
-import (
- "encoding/json"
- "flag"
- "fmt"
- "io/ioutil"
- "log"
- "math/rand"
- "os"
- "sync"
- "time"
-
- "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints"
- debuglet "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller"
- "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector"
- "cloud.google.com/go/compute/metadata"
- "golang.org/x/debug"
- "golang.org/x/debug/local"
- "golang.org/x/net/context"
- "golang.org/x/oauth2"
- "golang.org/x/oauth2/google"
- cd "google.golang.org/api/clouddebugger/v2"
-)
-
-var (
- appModule = flag.String("appmodule", "", "Optional application module name.")
- appVersion = flag.String("appversion", "", "Optional application module version name.")
- sourceContextFile = flag.String("sourcecontext", "", "File containing JSON-encoded source context.")
- verbose = flag.Bool("v", false, "Output verbose log messages.")
- projectNumber = flag.String("projectnumber", "", "Project number."+
- " If this is not set, it is read from the GCP metadata server.")
- projectID = flag.String("projectid", "", "Project ID."+
- " If this is not set, it is read from the GCP metadata server.")
- serviceAccountFile = flag.String("serviceaccountfile", "", "File containing JSON service account credentials.")
-)
-
-const (
- maxCapturedStackFrames = 50
- maxCapturedVariables = 1000
-)
-
-func main() {
- flag.Usage = usage
- flag.Parse()
- args := flag.Args()
- if len(args) == 0 {
- // The user needs to supply the name of the executable to run.
- flag.Usage()
- return
- }
- if *projectNumber == "" {
- var err error
- *projectNumber, err = metadata.NumericProjectID()
- if err != nil {
- log.Print("Debuglet initialization: ", err)
- }
- }
- if *projectID == "" {
- var err error
- *projectID, err = metadata.ProjectID()
- if err != nil {
- log.Print("Debuglet initialization: ", err)
- }
- }
- sourceContexts, err := readSourceContextFile(*sourceContextFile)
- if err != nil {
- log.Print("Reading source context file: ", err)
- }
- var ts oauth2.TokenSource
- ctx := context.Background()
- if *serviceAccountFile != "" {
- if ts, err = serviceAcctTokenSource(ctx, *serviceAccountFile, cd.CloudDebuggerScope); err != nil {
- log.Fatalf("Error getting credentials from file %s: %v", *serviceAccountFile, err)
- }
- } else if ts, err = google.DefaultTokenSource(ctx, cd.CloudDebuggerScope); err != nil {
- log.Print("Error getting application default credentials for Cloud Debugger:", err)
- os.Exit(103)
- }
- c, err := debuglet.NewController(ctx, debuglet.Options{
- ProjectNumber: *projectNumber,
- ProjectID: *projectID,
- AppModule: *appModule,
- AppVersion: *appVersion,
- SourceContexts: sourceContexts,
- Verbose: *verbose,
- TokenSource: ts,
- })
- if err != nil {
- log.Fatal("Error connecting to Cloud Debugger: ", err)
- }
- prog, err := local.New(args[0])
- if err != nil {
- log.Fatal("Error loading program: ", err)
- }
- // Load the program, but don't actually start it running yet.
- if _, err = prog.Run(args[1:]...); err != nil {
- log.Fatal("Error loading program: ", err)
- }
- bs := breakpoints.NewBreakpointStore(prog)
-
- // Seed the random number generator.
- rand.Seed(time.Now().UnixNano())
-
- // Now we want to do two things: run the user's program, and start sending
- // List requests periodically to the Debuglet Controller to get breakpoints
- // to set.
- //
- // We want to give the Debuglet Controller a chance to give us breakpoints
- // before we start the program, otherwise we would miss any breakpoint
- // triggers that occur during program startup -- for example, a breakpoint on
- // the first line of main. But if the Debuglet Controller is not responding or
- // is returning errors, we don't want to delay starting the program
- // indefinitely.
- //
- // We pass a channel to breakpointListLoop, which will close it when the first
- // List call finishes. Then we wait until either the channel is closed or a
- // 5-second timer has finished before starting the program.
- ch := make(chan bool)
- // Start a goroutine that sends List requests to the Debuglet Controller, and
- // sets any breakpoints it gets back.
- go breakpointListLoop(ctx, c, bs, ch)
- // Wait until 5 seconds have passed or breakpointListLoop has closed ch.
- select {
- case <-time.After(5 * time.Second):
- case <-ch:
- }
- // Run the debuggee.
- programLoop(ctx, c, bs, prog)
-}
-
-// usage prints a usage message to stderr and exits.
-func usage() {
- me := "a.out"
- if len(os.Args) >= 1 {
- me = os.Args[0]
- }
- fmt.Fprintf(os.Stderr, "Usage of %s:\n", me)
- fmt.Fprintf(os.Stderr, "\t%s [flags...] -- <program name> args...\n", me)
- fmt.Fprintf(os.Stderr, "Flags:\n")
- flag.PrintDefaults()
- fmt.Fprintf(os.Stderr,
- "See https://cloud.google.com/tools/cloud-debugger/setting-up-on-compute-engine for more information.\n")
- os.Exit(2)
-}
-
-// readSourceContextFile reads a JSON-encoded source context from the given file.
-// It returns a non-empty slice on success.
-func readSourceContextFile(filename string) ([]*cd.SourceContext, error) {
- if filename == "" {
- return nil, nil
- }
- scJSON, err := ioutil.ReadFile(filename)
- if err != nil {
- return nil, fmt.Errorf("reading file %q: %v", filename, err)
- }
- var sc cd.SourceContext
- if err = json.Unmarshal(scJSON, &sc); err != nil {
- return nil, fmt.Errorf("parsing file %q: %v", filename, err)
- }
- return []*cd.SourceContext{&sc}, nil
-}
-
-// breakpointListLoop repeatedly calls the Debuglet Controller's List RPC, and
-// passes the results to the BreakpointStore so it can set and unset breakpoints
-// in the program.
-//
-// After the first List call finishes, ch is closed.
-func breakpointListLoop(ctx context.Context, c *debuglet.Controller, bs *breakpoints.BreakpointStore, first chan bool) {
- const (
- avgTimeBetweenCalls = time.Second
- errorDelay = 5 * time.Second
- )
-
- // randomDuration returns a random duration with expected value avg.
- randomDuration := func(avg time.Duration) time.Duration {
- return time.Duration(rand.Int63n(int64(2*avg + 1)))
- }
-
- var consecutiveFailures uint
-
- for {
- callStart := time.Now()
- resp, err := c.List(ctx)
- if err != nil && err != debuglet.ErrListUnchanged {
- log.Printf("Debuglet controller server error: %v", err)
- }
- if err == nil {
- bs.ProcessBreakpointList(resp.Breakpoints)
- }
-
- if first != nil {
- // We've finished one call to List and set any breakpoints we received.
- close(first)
- first = nil
- }
-
- // Asynchronously send updates for any breakpoints that caused an error when
- // the BreakpointStore tried to process them. We don't wait for the update
- // to finish before the program can exit, as we do for normal updates.
- errorBps := bs.ErrorBreakpoints()
- for _, bp := range errorBps {
- go func(bp *cd.Breakpoint) {
- if err := c.Update(ctx, bp.Id, bp); err != nil {
- log.Printf("Failed to send breakpoint update for %s: %s", bp.Id, err)
- }
- }(bp)
- }
-
- // Make the next call not too soon after the one we just did.
- delay := randomDuration(avgTimeBetweenCalls)
-
- // If the call returned an error other than ErrListUnchanged, wait longer.
- if err != nil && err != debuglet.ErrListUnchanged {
- // Wait twice as long after each consecutive failure, to a maximum of 16x.
- delay += randomDuration(errorDelay * (1 << consecutiveFailures))
- if consecutiveFailures < 4 {
- consecutiveFailures++
- }
- } else {
- consecutiveFailures = 0
- }
-
- // Sleep until we reach time callStart+delay. If we've already passed that
- // time, time.Sleep will return immediately -- this should be the common
- // case, since the server will delay responding to List for a while when
- // there are no changes to report.
- time.Sleep(callStart.Add(delay).Sub(time.Now()))
- }
-}
-
-// programLoop runs the program being debugged to completion. When a breakpoint's
-// conditions are satisfied, it sends an Update RPC to the Debuglet Controller.
-// The function returns when the program exits and all Update RPCs have finished.
-func programLoop(ctx context.Context, c *debuglet.Controller, bs *breakpoints.BreakpointStore, prog debug.Program) {
- var wg sync.WaitGroup
- for {
- // Run the program until it hits a breakpoint or exits.
- status, err := prog.Resume()
- if err != nil {
- break
- }
-
- // Get the breakpoints at this address whose conditions were satisfied,
- // and remove the ones that aren't logpoints.
- bps := bs.BreakpointsAtPC(status.PC)
- bps = bpsWithConditionSatisfied(bps, prog)
- for _, bp := range bps {
- if bp.Action != "LOG" {
- bs.RemoveBreakpoint(bp)
- }
- }
-
- if len(bps) == 0 {
- continue
- }
-
- // Evaluate expressions and get the stack.
- vc := valuecollector.NewCollector(prog, maxCapturedVariables)
- needStackFrames := false
- for _, bp := range bps {
- // If evaluating bp's condition didn't return an error, evaluate bp's
- // expressions, and later get the stack frames.
- if bp.Status == nil {
- bp.EvaluatedExpressions = expressionValues(bp.Expressions, prog, vc)
- needStackFrames = true
- }
- }
- var (
- stack []*cd.StackFrame
- stackFramesStatusMessage *cd.StatusMessage
- )
- if needStackFrames {
- stack, stackFramesStatusMessage = stackFrames(prog, vc)
- }
-
- // Read variable values from the program.
- variableTable := vc.ReadValues()
-
- // Start a goroutine to send updates to the Debuglet Controller or write
- // to logs, concurrently with resuming the program.
- // TODO: retry Update on failure.
- for _, bp := range bps {
- wg.Add(1)
- switch bp.Action {
- case "LOG":
- go func(format string, evaluatedExpressions []*cd.Variable) {
- s := valuecollector.LogString(format, evaluatedExpressions, variableTable)
- log.Print(s)
- wg.Done()
- }(bp.LogMessageFormat, bp.EvaluatedExpressions)
- bp.Status = nil
- bp.EvaluatedExpressions = nil
- default:
- go func(bp *cd.Breakpoint) {
- defer wg.Done()
- bp.IsFinalState = true
- if bp.Status == nil {
- // If evaluating bp's condition didn't return an error, include the
- // stack frames, variable table, and any status message produced when
- // getting the stack frames.
- bp.StackFrames = stack
- bp.VariableTable = variableTable
- bp.Status = stackFramesStatusMessage
- }
- if err := c.Update(ctx, bp.Id, bp); err != nil {
- log.Printf("Failed to send breakpoint update for %s: %s", bp.Id, err)
- }
- }(bp)
- }
- }
- }
-
- // Wait for all updates to finish before returning.
- wg.Wait()
-}
-
-// bpsWithConditionSatisfied returns the breakpoints whose conditions are true
-// (or that do not have a condition.)
-func bpsWithConditionSatisfied(bpsIn []*cd.Breakpoint, prog debug.Program) []*cd.Breakpoint {
- var bpsOut []*cd.Breakpoint
- for _, bp := range bpsIn {
- cond, err := condTruth(bp.Condition, prog)
- if err != nil {
- bp.Status = errorStatusMessage(err.Error(), refersToBreakpointCondition)
- // Include bp in the list to be updated when there's an error, so that
- // the user gets a response.
- bpsOut = append(bpsOut, bp)
- } else if cond {
- bpsOut = append(bpsOut, bp)
- }
- }
- return bpsOut
-}
-
-// condTruth evaluates a condition.
-func condTruth(condition string, prog debug.Program) (bool, error) {
- if condition == "" {
- // A condition wasn't set.
- return true, nil
- }
- val, err := prog.Evaluate(condition)
- if err != nil {
- return false, err
- }
- if v, ok := val.(bool); !ok {
- return false, fmt.Errorf("condition expression has type %T, should be bool", val)
- } else {
- return v, nil
- }
-}
-
-// expressionValues evaluates a slice of expressions and returns a []*cd.Variable
-// containing the results.
-// If the result of an expression evaluation refers to values from the program's
-// memory (e.g., the expression evaluates to a slice) a corresponding variable is
-// added to the value collector, to be read later.
-func expressionValues(expressions []string, prog debug.Program, vc *valuecollector.Collector) []*cd.Variable {
- evaluatedExpressions := make([]*cd.Variable, len(expressions))
- for i, exp := range expressions {
- ee := &cd.Variable{Name: exp}
- evaluatedExpressions[i] = ee
- if val, err := prog.Evaluate(exp); err != nil {
- ee.Status = errorStatusMessage(err.Error(), refersToBreakpointExpression)
- } else {
- vc.FillValue(val, ee)
- }
- }
- return evaluatedExpressions
-}
-
-// stackFrames returns a stack trace for the program. It passes references to
-// function parameters and local variables to the value collector, so it can read
-// their values later.
-func stackFrames(prog debug.Program, vc *valuecollector.Collector) ([]*cd.StackFrame, *cd.StatusMessage) {
- frames, err := prog.Frames(maxCapturedStackFrames)
- if err != nil {
- return nil, errorStatusMessage("Error getting stack: "+err.Error(), refersToUnspecified)
- }
- stackFrames := make([]*cd.StackFrame, len(frames))
- for i, f := range frames {
- frame := &cd.StackFrame{}
- frame.Function = f.Function
- for _, v := range f.Params {
- frame.Arguments = append(frame.Arguments, vc.AddVariable(debug.LocalVar(v)))
- }
- for _, v := range f.Vars {
- frame.Locals = append(frame.Locals, vc.AddVariable(v))
- }
- frame.Location = &cd.SourceLocation{
- Path: f.File,
- Line: int64(f.Line),
- }
- stackFrames[i] = frame
- }
- return stackFrames, nil
-}
-
-// errorStatusMessage returns a *cd.StatusMessage indicating an error,
-// with the given message and refersTo field.
-func errorStatusMessage(msg string, refersTo int) *cd.StatusMessage {
- return &cd.StatusMessage{
- Description: &cd.FormatMessage{Format: "$0", Parameters: []string{msg}},
- IsError: true,
- RefersTo: refersToString[refersTo],
- }
-}
-
-const (
- // RefersTo values for cd.StatusMessage.
- refersToUnspecified = iota
- refersToBreakpointCondition
- refersToBreakpointExpression
-)
-
-// refersToString contains the strings for each refersTo value.
-// See the definition of StatusMessage in the v2/clouddebugger package.
-var refersToString = map[int]string{
- refersToUnspecified: "UNSPECIFIED",
- refersToBreakpointCondition: "BREAKPOINT_CONDITION",
- refersToBreakpointExpression: "BREAKPOINT_EXPRESSION",
-}
-
-func serviceAcctTokenSource(ctx context.Context, filename string, scope ...string) (oauth2.TokenSource, error) {
- data, err := ioutil.ReadFile(filename)
- if err != nil {
- return nil, fmt.Errorf("cannot read service account file: %v", err)
- }
- cfg, err := google.JWTConfigFromJSON(data, scope...)
- if err != nil {
- return nil, fmt.Errorf("google.JWTConfigFromJSON: %v", err)
- }
- return cfg.TokenSource(ctx), nil
-}
diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints.go
deleted file mode 100644
index afe07cbfb..000000000
--- a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints.go
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// 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 breakpoints handles breakpoint requests we get from the user through
-// the Debuglet Controller, and manages corresponding breakpoints set in the code.
-package breakpoints
-
-import (
- "log"
- "sync"
-
- "golang.org/x/debug"
- cd "google.golang.org/api/clouddebugger/v2"
-)
-
-// BreakpointStore stores the set of breakpoints for a program.
-type BreakpointStore struct {
- mu sync.Mutex
- // prog is the program being debugged.
- prog debug.Program
- // idToBreakpoint is a map from breakpoint identifier to *cd.Breakpoint. The
- // map value is nil if the breakpoint is inactive. A breakpoint is active if:
- // - We received it from the Debuglet Controller, and it was active at the time;
- // - We were able to set code breakpoints for it;
- // - We have not reached any of those code breakpoints while satisfying the
- // breakpoint's conditions, or the breakpoint has action LOG; and
- // - The Debuglet Controller hasn't informed us the breakpoint has become inactive.
- idToBreakpoint map[string]*cd.Breakpoint
- // pcToBps and bpToPCs store the many-to-many relationship between breakpoints we
- // received from the Debuglet Controller and the code breakpoints we set for them.
- pcToBps map[uint64][]*cd.Breakpoint
- bpToPCs map[*cd.Breakpoint][]uint64
- // errors contains any breakpoints which couldn't be set because they caused an
- // error. These are retrieved with ErrorBreakpoints, and the caller is
- // expected to handle sending updates for them.
- errors []*cd.Breakpoint
-}
-
-// NewBreakpointStore returns a BreakpointStore for the given program.
-func NewBreakpointStore(prog debug.Program) *BreakpointStore {
- return &BreakpointStore{
- idToBreakpoint: make(map[string]*cd.Breakpoint),
- pcToBps: make(map[uint64][]*cd.Breakpoint),
- bpToPCs: make(map[*cd.Breakpoint][]uint64),
- prog: prog,
- }
-}
-
-// ProcessBreakpointList applies updates received from the Debuglet Controller through a List call.
-func (bs *BreakpointStore) ProcessBreakpointList(bps []*cd.Breakpoint) {
- bs.mu.Lock()
- defer bs.mu.Unlock()
- for _, bp := range bps {
- if storedBp, ok := bs.idToBreakpoint[bp.Id]; ok {
- if storedBp != nil && bp.IsFinalState {
- // IsFinalState indicates that the breakpoint has been made inactive.
- bs.removeBreakpointLocked(storedBp)
- }
- } else {
- if bp.IsFinalState {
- // The controller is notifying us that the breakpoint is no longer active,
- // but we didn't know about it anyway.
- continue
- }
- if bp.Action != "" && bp.Action != "CAPTURE" && bp.Action != "LOG" {
- bp.IsFinalState = true
- bp.Status = &cd.StatusMessage{
- Description: &cd.FormatMessage{Format: "Action is not supported"},
- IsError: true,
- }
- bs.errors = append(bs.errors, bp)
- // Note in idToBreakpoint that we've already seen this breakpoint, so that we
- // don't try to report it as an error multiple times.
- bs.idToBreakpoint[bp.Id] = nil
- continue
- }
- pcs, err := bs.prog.BreakpointAtLine(bp.Location.Path, uint64(bp.Location.Line))
- if err != nil {
- log.Printf("error setting breakpoint at %s:%d: %v", bp.Location.Path, bp.Location.Line, err)
- }
- if len(pcs) == 0 {
- // We can't find a PC for this breakpoint's source line, so don't make it active.
- // TODO: we could snap the line to a location where we can break, or report an error to the user.
- bs.idToBreakpoint[bp.Id] = nil
- } else {
- bs.idToBreakpoint[bp.Id] = bp
- for _, pc := range pcs {
- bs.pcToBps[pc] = append(bs.pcToBps[pc], bp)
- }
- bs.bpToPCs[bp] = pcs
- }
- }
- }
-}
-
-// ErrorBreakpoints returns a slice of Breakpoints that caused errors when the
-// BreakpointStore tried to process them, and resets the list of such
-// breakpoints.
-// The caller is expected to send updates to the server to indicate the errors.
-func (bs *BreakpointStore) ErrorBreakpoints() []*cd.Breakpoint {
- bs.mu.Lock()
- defer bs.mu.Unlock()
- bps := bs.errors
- bs.errors = nil
- return bps
-}
-
-// BreakpointsAtPC returns all the breakpoints for which we set a code
-// breakpoint at the given address.
-func (bs *BreakpointStore) BreakpointsAtPC(pc uint64) []*cd.Breakpoint {
- bs.mu.Lock()
- defer bs.mu.Unlock()
- return bs.pcToBps[pc]
-}
-
-// RemoveBreakpoint makes the given breakpoint inactive.
-// This is called when either the debugged program hits the breakpoint, or the Debuglet
-// Controller informs us that the breakpoint is now inactive.
-func (bs *BreakpointStore) RemoveBreakpoint(bp *cd.Breakpoint) {
- bs.mu.Lock()
- bs.removeBreakpointLocked(bp)
- bs.mu.Unlock()
-}
-
-func (bs *BreakpointStore) removeBreakpointLocked(bp *cd.Breakpoint) {
- // Set the ID's corresponding breakpoint to nil, so that we won't activate it
- // if we see it again.
- // TODO: we could delete it after a few seconds.
- bs.idToBreakpoint[bp.Id] = nil
-
- // Delete bp from the list of cd breakpoints at each of its corresponding
- // code breakpoint locations, and delete any code breakpoints which no longer
- // have a corresponding cd breakpoint.
- var codeBreakpointsToDelete []uint64
- for _, pc := range bs.bpToPCs[bp] {
- bps := remove(bs.pcToBps[pc], bp)
- if len(bps) == 0 {
- // bp was the last breakpoint set at this PC, so delete the code breakpoint.
- codeBreakpointsToDelete = append(codeBreakpointsToDelete, pc)
- delete(bs.pcToBps, pc)
- } else {
- bs.pcToBps[pc] = bps
- }
- }
- if len(codeBreakpointsToDelete) > 0 {
- bs.prog.DeleteBreakpoints(codeBreakpointsToDelete)
- }
- delete(bs.bpToPCs, bp)
-}
-
-// remove updates rs by removing r, then returns rs.
-// The mutex in the BreakpointStore which contains rs should be held.
-func remove(rs []*cd.Breakpoint, r *cd.Breakpoint) []*cd.Breakpoint {
- for i := range rs {
- if rs[i] == r {
- rs[i] = rs[len(rs)-1]
- rs = rs[0 : len(rs)-1]
- return rs
- }
- }
- // We shouldn't reach here.
- return rs
-}
diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints_test.go
deleted file mode 100644
index 089a3ba61..000000000
--- a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints_test.go
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// 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 breakpoints
-
-import (
- "reflect"
- "testing"
-
- "golang.org/x/debug"
- cd "google.golang.org/api/clouddebugger/v2"
-)
-
-var (
- testPC1 uint64 = 0x1234
- testPC2 uint64 = 0x5678
- testPC3 uint64 = 0x3333
- testFile = "foo.go"
- testLine uint64 = 42
- testLine2 uint64 = 99
- testLogPC uint64 = 0x9abc
- testLogLine uint64 = 43
- testBadPC uint64 = 0xdef0
- testBadLine uint64 = 44
- testBP = &cd.Breakpoint{
- Action: "CAPTURE",
- Id: "TestBreakpoint",
- IsFinalState: false,
- Location: &cd.SourceLocation{Path: testFile, Line: int64(testLine)},
- }
- testBP2 = &cd.Breakpoint{
- Action: "CAPTURE",
- Id: "TestBreakpoint2",
- IsFinalState: false,
- Location: &cd.SourceLocation{Path: testFile, Line: int64(testLine2)},
- }
- testLogBP = &cd.Breakpoint{
- Action: "LOG",
- Id: "TestLogBreakpoint",
- IsFinalState: false,
- Location: &cd.SourceLocation{Path: testFile, Line: int64(testLogLine)},
- }
- testBadBP = &cd.Breakpoint{
- Action: "BEEP",
- Id: "TestBadBreakpoint",
- IsFinalState: false,
- Location: &cd.SourceLocation{Path: testFile, Line: int64(testBadLine)},
- }
-)
-
-func TestBreakpointStore(t *testing.T) {
- p := &Program{breakpointPCs: make(map[uint64]bool)}
- bs := NewBreakpointStore(p)
- checkPCs := func(expected map[uint64]bool) {
- if !reflect.DeepEqual(p.breakpointPCs, expected) {
- t.Errorf("got breakpoint map %v want %v", p.breakpointPCs, expected)
- }
- }
- bs.ProcessBreakpointList([]*cd.Breakpoint{testBP, testBP2, testLogBP, testBadBP})
- checkPCs(map[uint64]bool{
- testPC1: true,
- testPC2: true,
- testPC3: true,
- testLogPC: true,
- })
- for _, test := range []struct {
- pc uint64
- expected []*cd.Breakpoint
- }{
- {testPC1, []*cd.Breakpoint{testBP}},
- {testPC2, []*cd.Breakpoint{testBP}},
- {testPC3, []*cd.Breakpoint{testBP2}},
- {testLogPC, []*cd.Breakpoint{testLogBP}},
- } {
- if bps := bs.BreakpointsAtPC(test.pc); !reflect.DeepEqual(bps, test.expected) {
- t.Errorf("BreakpointsAtPC(%x): got %v want %v", test.pc, bps, test.expected)
- }
- }
- testBP2.IsFinalState = true
- bs.ProcessBreakpointList([]*cd.Breakpoint{testBP, testBP2, testLogBP, testBadBP})
- checkPCs(map[uint64]bool{
- testPC1: true,
- testPC2: true,
- testPC3: false,
- testLogPC: true,
- })
- bs.RemoveBreakpoint(testBP)
- checkPCs(map[uint64]bool{
- testPC1: false,
- testPC2: false,
- testPC3: false,
- testLogPC: true,
- })
- for _, pc := range []uint64{testPC1, testPC2, testPC3} {
- if bps := bs.BreakpointsAtPC(pc); len(bps) != 0 {
- t.Errorf("BreakpointsAtPC(%x): got %v want []", pc, bps)
- }
- }
- // bs.ErrorBreakpoints should return testBadBP.
- errorBps := bs.ErrorBreakpoints()
- if len(errorBps) != 1 {
- t.Errorf("ErrorBreakpoints: got %d want 1", len(errorBps))
- } else {
- bp := errorBps[0]
- if bp.Id != testBadBP.Id {
- t.Errorf("ErrorBreakpoints: got id %q want 1", bp.Id)
- }
- if bp.Status == nil || !bp.Status.IsError {
- t.Errorf("ErrorBreakpoints: got %v, want error", bp.Status)
- }
- }
- // The error should have been removed by the last call to bs.ErrorBreakpoints.
- errorBps = bs.ErrorBreakpoints()
- if len(errorBps) != 0 {
- t.Errorf("ErrorBreakpoints: got %d want 0", len(errorBps))
- }
- // Even if testBadBP is sent in a new list, it should not be returned again.
- bs.ProcessBreakpointList([]*cd.Breakpoint{testBadBP})
- errorBps = bs.ErrorBreakpoints()
- if len(errorBps) != 0 {
- t.Errorf("ErrorBreakpoints: got %d want 0", len(errorBps))
- }
-}
-
-// Program implements the similarly-named interface in x/debug.
-// ValueCollector should only call its BreakpointAtLine and DeleteBreakpoints methods.
-type Program struct {
- debug.Program
- // breakpointPCs contains the state of code breakpoints -- true if the
- // breakpoint is currently set, false if it has been deleted.
- breakpointPCs map[uint64]bool
-}
-
-func (p *Program) BreakpointAtLine(file string, line uint64) ([]uint64, error) {
- var pcs []uint64
- switch {
- case file == testFile && line == testLine:
- pcs = []uint64{testPC1, testPC2}
- case file == testFile && line == testLine2:
- pcs = []uint64{testPC3}
- case file == testFile && line == testLogLine:
- pcs = []uint64{testLogPC}
- default:
- pcs = []uint64{0xbad}
- }
- for _, pc := range pcs {
- p.breakpointPCs[pc] = true
- }
- return pcs, nil
-}
-
-func (p *Program) DeleteBreakpoints(pcs []uint64) error {
- for _, pc := range pcs {
- p.breakpointPCs[pc] = false
- }
- return nil
-}
diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client.go
deleted file mode 100644
index 87d283546..000000000
--- a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client.go
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// 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 controller is a library for interacting with the Google Cloud Debugger's Debuglet Controller service.
-package controller
-
-import (
- "crypto/sha256"
- "encoding/json"
- "errors"
- "fmt"
- "log"
- "sync"
-
- "golang.org/x/net/context"
- "golang.org/x/oauth2"
- cd "google.golang.org/api/clouddebugger/v2"
- "google.golang.org/api/googleapi"
- "google.golang.org/api/option"
- "google.golang.org/api/transport"
-)
-
-const (
- // agentVersionString identifies the agent to the service.
- agentVersionString = "google.com/go-gcp/v0.2"
- // initWaitToken is the wait token sent in the first Update request to a server.
- initWaitToken = "init"
-)
-
-var (
- // ErrListUnchanged is returned by List if the server time limit is reached
- // before the list of breakpoints changes.
- ErrListUnchanged = errors.New("breakpoint list unchanged")
- // ErrDebuggeeDisabled is returned by List or Update if the server has disabled
- // this Debuggee. The caller can retry later.
- ErrDebuggeeDisabled = errors.New("debuglet disabled by server")
-)
-
-// Controller manages a connection to the Debuglet Controller service.
-type Controller struct {
- s serviceInterface
- // waitToken is sent with List requests so the server knows which set of
- // breakpoints this client has already seen. Each successful List request
- // returns a new waitToken to send in the next request.
- waitToken string
- // verbose determines whether to do some logging
- verbose bool
- // options, uniquifier and description are used in register.
- options Options
- uniquifier string
- description string
- // labels are included when registering the debuggee. They should contain
- // the module name, version and minorversion, and are used by the debug UI
- // to label the correct version active for debugging.
- labels map[string]string
- // mu protects debuggeeID
- mu sync.Mutex
- // debuggeeID is returned from the server on registration, and is passed back
- // to the server in List and Update requests.
- debuggeeID string
-}
-
-// Options controls how the Debuglet Controller client identifies itself to the server.
-// See https://cloud.google.com/storage/docs/projects and
-// https://cloud.google.com/tools/cloud-debugger/setting-up-on-compute-engine
-// for further documentation of these parameters.
-type Options struct {
- ProjectNumber string // GCP Project Number.
- ProjectID string // GCP Project ID.
- AppModule string // Module name for the debugged program.
- AppVersion string // Version number for this module.
- SourceContexts []*cd.SourceContext // Description of source.
- Verbose bool
- TokenSource oauth2.TokenSource // Source of Credentials used for Stackdriver Debugger.
-}
-
-type serviceInterface interface {
- Register(ctx context.Context, req *cd.RegisterDebuggeeRequest) (*cd.RegisterDebuggeeResponse, error)
- Update(ctx context.Context, debuggeeID, breakpointID string, req *cd.UpdateActiveBreakpointRequest) (*cd.UpdateActiveBreakpointResponse, error)
- List(ctx context.Context, debuggeeID, waitToken string) (*cd.ListActiveBreakpointsResponse, error)
-}
-
-var newService = func(ctx context.Context, tokenSource oauth2.TokenSource) (serviceInterface, error) {
- httpClient, endpoint, err := transport.NewHTTPClient(ctx, option.WithTokenSource(tokenSource))
- if err != nil {
- return nil, err
- }
- s, err := cd.New(httpClient)
- if err != nil {
- return nil, err
- }
- if endpoint != "" {
- s.BasePath = endpoint
- }
- return &service{s: s}, nil
-}
-
-type service struct {
- s *cd.Service
-}
-
-func (s service) Register(ctx context.Context, req *cd.RegisterDebuggeeRequest) (*cd.RegisterDebuggeeResponse, error) {
- call := cd.NewControllerDebuggeesService(s.s).Register(req)
- return call.Context(ctx).Do()
-}
-
-func (s service) Update(ctx context.Context, debuggeeID, breakpointID string, req *cd.UpdateActiveBreakpointRequest) (*cd.UpdateActiveBreakpointResponse, error) {
- call := cd.NewControllerDebuggeesBreakpointsService(s.s).Update(debuggeeID, breakpointID, req)
- return call.Context(ctx).Do()
-}
-
-func (s service) List(ctx context.Context, debuggeeID, waitToken string) (*cd.ListActiveBreakpointsResponse, error) {
- call := cd.NewControllerDebuggeesBreakpointsService(s.s).List(debuggeeID)
- call.WaitToken(waitToken)
- return call.Context(ctx).Do()
-}
-
-// NewController connects to the Debuglet Controller server using the given options,
-// and returns a Controller for that connection.
-// Google Application Default Credentials are used to connect to the Debuglet Controller;
-// see https://developers.google.com/identity/protocols/application-default-credentials
-func NewController(ctx context.Context, o Options) (*Controller, error) {
- // We build a JSON encoding of o.SourceContexts so we can hash it.
- scJSON, err := json.Marshal(o.SourceContexts)
- if err != nil {
- scJSON = nil
- o.SourceContexts = nil
- }
- const minorversion = "107157" // any arbitrary numeric string
-
- // Compute a uniquifier string by hashing the project number, app module name,
- // app module version, debuglet version, and source context.
- // The choice of hash function is arbitrary.
- h := sha256.Sum256([]byte(fmt.Sprintf("%d %s %d %s %d %s %d %s %d %s %d %s",
- len(o.ProjectNumber), o.ProjectNumber,
- len(o.AppModule), o.AppModule,
- len(o.AppVersion), o.AppVersion,
- len(agentVersionString), agentVersionString,
- len(scJSON), scJSON,
- len(minorversion), minorversion)))
- uniquifier := fmt.Sprintf("%X", h[0:16]) // 32 hex characters
-
- description := o.ProjectID
- if o.AppModule != "" {
- description += "-" + o.AppModule
- }
- if o.AppVersion != "" {
- description += "-" + o.AppVersion
- }
-
- s, err := newService(ctx, o.TokenSource)
- if err != nil {
- return nil, err
- }
-
- // Construct client.
- c := &Controller{
- s: s,
- waitToken: initWaitToken,
- verbose: o.Verbose,
- options: o,
- uniquifier: uniquifier,
- description: description,
- labels: map[string]string{
- "module": o.AppModule,
- "version": o.AppVersion,
- "minorversion": minorversion,
- },
- }
-
- return c, nil
-}
-
-func (c *Controller) getDebuggeeID(ctx context.Context) (string, error) {
- c.mu.Lock()
- defer c.mu.Unlock()
- if c.debuggeeID != "" {
- return c.debuggeeID, nil
- }
- // The debuglet hasn't been registered yet, or it is disabled and we should try registering again.
- if err := c.register(ctx); err != nil {
- return "", err
- }
- return c.debuggeeID, nil
-}
-
-// List retrieves the current list of breakpoints from the server.
-// If the set of breakpoints on the server is the same as the one returned in
-// the previous call to List, the server can delay responding until it changes,
-// and return an error instead if no change occurs before a time limit the
-// server sets. List can't be called concurrently with itself.
-func (c *Controller) List(ctx context.Context) (*cd.ListActiveBreakpointsResponse, error) {
- id, err := c.getDebuggeeID(ctx)
- if err != nil {
- return nil, err
- }
- resp, err := c.s.List(ctx, id, c.waitToken)
- if err != nil {
- if isAbortedError(err) {
- return nil, ErrListUnchanged
- }
- // For other errors, the protocol requires that we attempt to re-register.
- c.mu.Lock()
- defer c.mu.Unlock()
- if regError := c.register(ctx); regError != nil {
- return nil, regError
- }
- return nil, err
- }
- if resp == nil {
- return nil, errors.New("no response")
- }
- if c.verbose {
- log.Printf("List response: %v", resp)
- }
- c.waitToken = resp.NextWaitToken
- return resp, nil
-}
-
-// isAbortedError tests if err is a *googleapi.Error, that it contains one error
-// in Errors, and that that error's Reason is "aborted".
-func isAbortedError(err error) bool {
- e, _ := err.(*googleapi.Error)
- if e == nil {
- return false
- }
- if len(e.Errors) != 1 {
- return false
- }
- return e.Errors[0].Reason == "aborted"
-}
-
-// Update reports information to the server about a breakpoint that was hit.
-// Update can be called concurrently with List and Update.
-func (c *Controller) Update(ctx context.Context, breakpointID string, bp *cd.Breakpoint) error {
- req := &cd.UpdateActiveBreakpointRequest{Breakpoint: bp}
- if c.verbose {
- log.Printf("sending update for %s: %v", breakpointID, req)
- }
- id, err := c.getDebuggeeID(ctx)
- if err != nil {
- return err
- }
- _, err = c.s.Update(ctx, id, breakpointID, req)
- return err
-}
-
-// register calls the Debuglet Controller Register method, and sets c.debuggeeID.
-// c.mu should be locked while calling this function. List and Update can't
-// make progress until it returns.
-func (c *Controller) register(ctx context.Context) error {
- req := cd.RegisterDebuggeeRequest{
- Debuggee: &cd.Debuggee{
- AgentVersion: agentVersionString,
- Description: c.description,
- Project: c.options.ProjectNumber,
- SourceContexts: c.options.SourceContexts,
- Uniquifier: c.uniquifier,
- Labels: c.labels,
- },
- }
- resp, err := c.s.Register(ctx, &req)
- if err != nil {
- return err
- }
- if resp == nil {
- return errors.New("register: no response")
- }
- if resp.Debuggee.IsDisabled {
- // Setting c.debuggeeID to empty makes sure future List and Update calls
- // will call register first.
- c.debuggeeID = ""
- } else {
- c.debuggeeID = resp.Debuggee.Id
- }
- if c.debuggeeID == "" {
- return ErrDebuggeeDisabled
- }
- return nil
-}
diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client_test.go
deleted file mode 100644
index fa0634706..000000000
--- a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/controller/client_test.go
+++ /dev/null
@@ -1,254 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// 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 controller
-
-import (
- "bytes"
- "errors"
- "fmt"
- "strconv"
- "testing"
-
- "golang.org/x/net/context"
- "golang.org/x/oauth2"
-
- cd "google.golang.org/api/clouddebugger/v2"
- "google.golang.org/api/googleapi"
-)
-
-const (
- testDebuggeeID = "d12345"
- testBreakpointID = "bp12345"
-)
-
-var (
- // The sequence of wait tokens in List requests and responses.
- expectedWaitToken = []string{"init", "token1", "token2", "token1", "token1"}
- // The set of breakpoints returned from each List call.
- expectedBreakpoints = [][]*cd.Breakpoint{
- nil,
- {
- &cd.Breakpoint{
- Id: testBreakpointID,
- IsFinalState: false,
- Location: &cd.SourceLocation{Line: 42, Path: "foo.go"},
- },
- },
- nil,
- }
- abortedError error = &googleapi.Error{
- Code: 409,
- Message: "Conflict",
- Body: `{
- "error": {
- "errors": [
- {
- "domain": "global",
- "reason": "aborted",
- "message": "Conflict"
- }
- ],
- "code": 409,
- "message": "Conflict"
- }
- }`,
- Errors: []googleapi.ErrorItem{
- {Reason: "aborted", Message: "Conflict"},
- },
- }
- backendError error = &googleapi.Error{
- Code: 503,
- Message: "Backend Error",
- Body: `{
- "error": {
- "errors": [
- {
- "domain": "global",
- "reason": "backendError",
- "message": "Backend Error"
- }
- ],
- "code": 503,
- "message": "Backend Error"
- }
- }`,
- Errors: []googleapi.ErrorItem{
- {Reason: "backendError", Message: "Backend Error"},
- },
- }
-)
-
-type mockService struct {
- t *testing.T
- listCallsSeen int
- registerCallsSeen int
-}
-
-func (s *mockService) Register(ctx context.Context, req *cd.RegisterDebuggeeRequest) (*cd.RegisterDebuggeeResponse, error) {
- s.registerCallsSeen++
- if req.Debuggee == nil {
- s.t.Errorf("missing debuggee")
- return nil, nil
- }
- if req.Debuggee.AgentVersion == "" {
- s.t.Errorf("missing agent version")
- }
- if req.Debuggee.Description == "" {
- s.t.Errorf("missing debuglet description")
- }
- if req.Debuggee.Project == "" {
- s.t.Errorf("missing project id")
- }
- if req.Debuggee.Uniquifier == "" {
- s.t.Errorf("missing uniquifier")
- }
- return &cd.RegisterDebuggeeResponse{
- Debuggee: &cd.Debuggee{Id: testDebuggeeID},
- }, nil
-}
-
-func (s *mockService) Update(ctx context.Context, id, breakpointID string, req *cd.UpdateActiveBreakpointRequest) (*cd.UpdateActiveBreakpointResponse, error) {
- if id != testDebuggeeID {
- s.t.Errorf("got debuggee ID %s want %s", id, testDebuggeeID)
- }
- if breakpointID != testBreakpointID {
- s.t.Errorf("got breakpoint ID %s want %s", breakpointID, testBreakpointID)
- }
- if !req.Breakpoint.IsFinalState {
- s.t.Errorf("got IsFinalState = false, want true")
- }
- return nil, nil
-}
-
-func (s *mockService) List(ctx context.Context, id, waitToken string) (*cd.ListActiveBreakpointsResponse, error) {
- if id != testDebuggeeID {
- s.t.Errorf("got debuggee ID %s want %s", id, testDebuggeeID)
- }
- if waitToken != expectedWaitToken[s.listCallsSeen] {
- s.t.Errorf("got wait token %s want %s", waitToken, expectedWaitToken[s.listCallsSeen])
- }
- s.listCallsSeen++
- if s.listCallsSeen == 4 {
- return nil, backendError
- }
- if s.listCallsSeen == 5 {
- return nil, abortedError
- }
- resp := &cd.ListActiveBreakpointsResponse{
- Breakpoints: expectedBreakpoints[s.listCallsSeen-1],
- NextWaitToken: expectedWaitToken[s.listCallsSeen],
- }
- return resp, nil
-}
-
-func TestDebugletControllerClientLibrary(t *testing.T) {
- var (
- m *mockService
- c *Controller
- list *cd.ListActiveBreakpointsResponse
- err error
- )
- m = &mockService{t: t}
- newService = func(context.Context, oauth2.TokenSource) (serviceInterface, error) { return m, nil }
- opts := Options{
- ProjectNumber: "5",
- ProjectID: "p1",
- AppModule: "mod1",
- AppVersion: "v1",
- }
- ctx := context.Background()
- if c, err = NewController(ctx, opts); err != nil {
- t.Fatal("Initializing Controller client:", err)
- }
- if err := validateLabels(c, opts); err != nil {
- t.Fatalf("Invalid labels:\n%v", err)
- }
- if list, err = c.List(ctx); err != nil {
- t.Fatal("List:", err)
- }
- if m.registerCallsSeen != 1 {
- t.Errorf("saw %d Register calls, want 1", m.registerCallsSeen)
- }
- if list, err = c.List(ctx); err != nil {
- t.Fatal("List:", err)
- }
- if len(list.Breakpoints) != 1 {
- t.Fatalf("got %d breakpoints, want 1", len(list.Breakpoints))
- }
- if err = c.Update(ctx, list.Breakpoints[0].Id, &cd.Breakpoint{Id: testBreakpointID, IsFinalState: true}); err != nil {
- t.Fatal("Update:", err)
- }
- if list, err = c.List(ctx); err != nil {
- t.Fatal("List:", err)
- }
- if m.registerCallsSeen != 1 {
- t.Errorf("saw %d Register calls, want 1", m.registerCallsSeen)
- }
- // The next List call produces an error that should cause a Register call.
- if list, err = c.List(ctx); err == nil {
- t.Fatal("List should have returned an error")
- }
- if m.registerCallsSeen != 2 {
- t.Errorf("saw %d Register calls, want 2", m.registerCallsSeen)
- }
- // The next List call produces an error that should not cause a Register call.
- if list, err = c.List(ctx); err == nil {
- t.Fatal("List should have returned an error")
- }
- if m.registerCallsSeen != 2 {
- t.Errorf("saw %d Register calls, want 2", m.registerCallsSeen)
- }
- if m.listCallsSeen != 5 {
- t.Errorf("saw %d list calls, want 5", m.listCallsSeen)
- }
-}
-
-func validateLabels(c *Controller, o Options) error {
- errMsg := new(bytes.Buffer)
- if m, ok := c.labels["module"]; ok {
- if m != o.AppModule {
- errMsg.WriteString(fmt.Sprintf("label module: want %s, got %s\n", o.AppModule, m))
- }
- } else {
- errMsg.WriteString("Missing \"module\" label\n")
- }
- if v, ok := c.labels["version"]; ok {
- if v != o.AppVersion {
- errMsg.WriteString(fmt.Sprintf("label version: want %s, got %s\n", o.AppVersion, v))
- }
- } else {
- errMsg.WriteString("Missing \"version\" label\n")
- }
- if mv, ok := c.labels["minorversion"]; ok {
- if _, err := strconv.Atoi(mv); err != nil {
- errMsg.WriteString(fmt.Sprintln("label minorversion: not a numeric string:", mv))
- }
- } else {
- errMsg.WriteString("Missing \"minorversion\" label\n")
- }
- if errMsg.Len() != 0 {
- return errors.New(errMsg.String())
- }
- return nil
-}
-
-func TestIsAbortedError(t *testing.T) {
- if !isAbortedError(abortedError) {
- t.Errorf("isAborted(%+v): got false, want true", abortedError)
- }
- if isAbortedError(backendError) {
- t.Errorf("isAborted(%+v): got true, want false", backendError)
- }
-}
diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector.go
deleted file mode 100644
index 8dadc2f63..000000000
--- a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector.go
+++ /dev/null
@@ -1,460 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// 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 valuecollector is used to collect the values of variables in a program.
-package valuecollector
-
-import (
- "bytes"
- "fmt"
- "strconv"
- "strings"
-
- "golang.org/x/debug"
- cd "google.golang.org/api/clouddebugger/v2"
-)
-
-const (
- maxArrayLength = 50
- maxMapLength = 20
-)
-
-// Collector is given references to variables from a program being debugged
-// using AddVariable. Then when ReadValues is called, the Collector will fetch
-// the values of those variables. Any variables referred to by those values
-// will also be fetched; e.g. the targets of pointers, members of structs,
-// elements of slices, etc. This continues iteratively, building a graph of
-// values, until all the reachable values are fetched, or a size limit is
-// reached.
-//
-// Variables are passed to the Collector as debug.Var, which is used by x/debug
-// to represent references to variables. Values are returned as cd.Variable,
-// which is used by the Debuglet Controller to represent the graph of values.
-//
-// For example, if the program has a struct variable:
-//
-// foo := SomeStruct{a:42, b:"xyz"}
-//
-// and we call AddVariable with a reference to foo, we will get back a result
-// like:
-//
-// cd.Variable{Name:"foo", VarTableIndex:10}
-//
-// which denotes a variable named "foo" which will have its value stored in
-// element 10 of the table that will later be returned by ReadValues. That
-// element might be:
-//
-// out[10] = &cd.Variable{Members:{{Name:"a", VarTableIndex:11},{Name:"b", VarTableIndex:12}}}
-//
-// which denotes a struct with two members a and b, whose values are in elements
-// 11 and 12 of the output table:
-//
-// out[11] = &cd.Variable{Value:"42"}
-// out[12] = &cd.Variable{Value:"xyz"}
-type Collector struct {
- // prog is the program being debugged.
- prog debug.Program
- // limit is the maximum size of the output slice of values.
- limit int
- // index is a map from references (variables and map elements) to their
- // locations in the table.
- index map[reference]int
- // table contains the references, including those given to the
- // Collector directly and those the Collector itself found.
- // If VarTableIndex is set to 0 in a cd.Variable, it is ignored, so the first entry
- // of table can't be used. On initialization we put a dummy value there.
- table []reference
-}
-
-// reference represents a value which is in the queue to be read by the
-// collector. It is either a debug.Var, or a mapElement.
-type reference interface{}
-
-// mapElement represents an element of a map in the debugged program's memory.
-type mapElement struct {
- debug.Map
- index uint64
-}
-
-// NewCollector returns a Collector for the given program and size limit.
-// The limit is the maximum size of the slice of values returned by ReadValues.
-func NewCollector(prog debug.Program, limit int) *Collector {
- return &Collector{
- prog: prog,
- limit: limit,
- index: make(map[reference]int),
- table: []reference{debug.Var{}},
- }
-}
-
-// AddVariable adds another variable to be collected.
-// The Collector doesn't get the value immediately; it returns a cd.Variable
-// that contains an index into the table which will later be returned by
-// ReadValues.
-func (c *Collector) AddVariable(lv debug.LocalVar) *cd.Variable {
- ret := &cd.Variable{Name: lv.Name}
- if index, ok := c.add(lv.Var); !ok {
- // If the add call failed, it's because we reached the size limit.
- // The Debuglet Controller's convention is to pass it a "Not Captured" error
- // in this case.
- ret.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
- } else {
- ret.VarTableIndex = int64(index)
- }
- return ret
-}
-
-// add adds a reference to the set of values to be read from the
-// program. It returns the index in the output table that will contain the
-// corresponding value. It fails if the table has reached the size limit.
-// It deduplicates references, so the index may be the same as one that was
-// returned from an earlier add call.
-func (c *Collector) add(r reference) (outputIndex int, ok bool) {
- if i, ok := c.index[r]; ok {
- return i, true
- }
- i := len(c.table)
- if i >= c.limit {
- return 0, false
- }
- c.index[r] = i
- c.table = append(c.table, r)
- return i, true
-}
-
-func addMember(v *cd.Variable, name string) *cd.Variable {
- v2 := &cd.Variable{Name: name}
- v.Members = append(v.Members, v2)
- return v2
-}
-
-// ReadValues fetches values of the variables that were passed to the Collector
-// with AddVariable. The values of any new variables found are also fetched,
-// e.g. the targets of pointers or the members of structs, until we reach the
-// size limit or we run out of values to fetch.
-// The results are output as a []*cd.Variable, which is the type we need to send
-// to the Debuglet Controller after we trigger a breakpoint.
-func (c *Collector) ReadValues() (out []*cd.Variable) {
- for i := 0; i < len(c.table); i++ {
- // Create a new cd.Variable for this value, and append it to the output.
- dcv := new(cd.Variable)
- out = append(out, dcv)
- if i == 0 {
- // The first element is unused.
- continue
- }
- switch x := c.table[i].(type) {
- case mapElement:
- key, value, err := c.prog.MapElement(x.Map, x.index)
- if err != nil {
- dcv.Status = statusMessage(err.Error(), true, refersToVariableValue)
- continue
- }
- // Add a member for the key.
- member := addMember(dcv, "key")
- if index, ok := c.add(key); !ok {
- // The table is full.
- member.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
- continue
- } else {
- member.VarTableIndex = int64(index)
- }
- // Add a member for the value.
- member = addMember(dcv, "value")
- if index, ok := c.add(value); !ok {
- // The table is full.
- member.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
- } else {
- member.VarTableIndex = int64(index)
- }
- case debug.Var:
- if v, err := c.prog.Value(x); err != nil {
- dcv.Status = statusMessage(err.Error(), true, refersToVariableValue)
- } else {
- c.FillValue(v, dcv)
- }
- }
- }
- return out
-}
-
-// indexable is an interface for arrays, slices and channels.
-type indexable interface {
- Len() uint64
- Element(uint64) debug.Var
-}
-
-// channel implements indexable.
-type channel struct {
- debug.Channel
-}
-
-func (c channel) Len() uint64 {
- return c.Length
-}
-
-var (
- _ indexable = debug.Array{}
- _ indexable = debug.Slice{}
- _ indexable = channel{}
-)
-
-// FillValue copies a value into a cd.Variable. Any variables referred to by
-// that value, e.g. struct members and pointer targets, are added to the
-// collector's queue, to be fetched later by ReadValues.
-func (c *Collector) FillValue(v debug.Value, dcv *cd.Variable) {
- if c, ok := v.(debug.Channel); ok {
- // Convert to channel, which implements indexable.
- v = channel{c}
- }
- // Fill in dcv in a manner depending on the type of the value we got.
- switch val := v.(type) {
- case int8, int16, int32, int64, bool, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128:
- // For simple types, we just print the value to dcv.Value.
- dcv.Value = fmt.Sprint(val)
- case string:
- // Put double quotes around strings.
- dcv.Value = strconv.Quote(val)
- case debug.String:
- if uint64(len(val.String)) < val.Length {
- // This string value was truncated.
- dcv.Value = strconv.Quote(val.String + "...")
- } else {
- dcv.Value = strconv.Quote(val.String)
- }
- case debug.Struct:
- // For structs, we add an entry to dcv.Members for each field in the
- // struct.
- // Each member will contain the name of the field, and the index in the
- // output table which will contain the value of that field.
- for _, f := range val.Fields {
- member := addMember(dcv, f.Name)
- if index, ok := c.add(f.Var); !ok {
- // The table is full.
- member.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
- } else {
- member.VarTableIndex = int64(index)
- }
- }
- case debug.Map:
- dcv.Value = fmt.Sprintf("len = %d", val.Length)
- for i := uint64(0); i < val.Length; i++ {
- field := addMember(dcv, `⚫`)
- if i == maxMapLength {
- field.Name = "..."
- field.Status = statusMessage(messageTruncated, true, refersToVariableName)
- break
- }
- if index, ok := c.add(mapElement{val, i}); !ok {
- // The value table is full; add a member to contain the error message.
- field.Name = "..."
- field.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
- break
- } else {
- field.VarTableIndex = int64(index)
- }
- }
- case debug.Pointer:
- if val.Address == 0 {
- dcv.Value = "<nil>"
- } else if val.TypeID == 0 {
- // We don't know the type of the pointer, so just output the address as
- // the value.
- dcv.Value = fmt.Sprintf("0x%X", val.Address)
- dcv.Status = statusMessage(messageUnknownPointerType, false, refersToVariableName)
- } else {
- // Adds the pointed-to variable to the table, and links this value to
- // that table entry through VarTableIndex.
- dcv.Value = fmt.Sprintf("0x%X", val.Address)
- target := addMember(dcv, "")
- if index, ok := c.add(debug.Var(val)); !ok {
- target.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
- } else {
- target.VarTableIndex = int64(index)
- }
- }
- case indexable:
- // Arrays, slices and channels.
- dcv.Value = "len = " + fmt.Sprint(val.Len())
- for j := uint64(0); j < val.Len(); j++ {
- field := addMember(dcv, fmt.Sprint(`[`, j, `]`))
- if j == maxArrayLength {
- field.Name = "..."
- field.Status = statusMessage(messageTruncated, true, refersToVariableName)
- break
- }
- vr := val.Element(j)
- if index, ok := c.add(vr); !ok {
- // The value table is full; add a member to contain the error message.
- field.Name = "..."
- field.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
- break
- } else {
- // Add a member with the index as the name.
- field.VarTableIndex = int64(index)
- }
- }
- default:
- dcv.Status = statusMessage(messageUnknownType, false, refersToVariableName)
- }
-}
-
-// statusMessage returns a *cd.StatusMessage with the given message, IsError
-// field and refersTo field.
-func statusMessage(msg string, isError bool, refersTo int) *cd.StatusMessage {
- return &cd.StatusMessage{
- Description: &cd.FormatMessage{Format: "$0", Parameters: []string{msg}},
- IsError: isError,
- RefersTo: refersToString[refersTo],
- }
-}
-
-// LogString produces a string for a logpoint, substituting in variable values
-// using evaluatedExpressions and varTable.
-func LogString(s string, evaluatedExpressions []*cd.Variable, varTable []*cd.Variable) string {
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "LOGPOINT: ")
- seen := make(map[*cd.Variable]bool)
- for i := 0; i < len(s); {
- if s[i] == '$' {
- i++
- if num, n, ok := parseToken(s[i:], len(evaluatedExpressions)-1); ok {
- // This token is one of $0, $1, etc. Write the corresponding expression.
- writeExpression(&buf, evaluatedExpressions[num], false, varTable, seen)
- i += n
- } else {
- // Something else, like $$.
- buf.WriteByte(s[i])
- i++
- }
- } else {
- buf.WriteByte(s[i])
- i++
- }
- }
- return buf.String()
-}
-
-func parseToken(s string, max int) (num int, bytesRead int, ok bool) {
- var i int
- for i < len(s) && s[i] >= '0' && s[i] <= '9' {
- i++
- }
- num, err := strconv.Atoi(s[:i])
- return num, i, err == nil && num <= max
-}
-
-// writeExpression recursively writes variables to buf, in a format suitable
-// for logging. If printName is true, writes the name of the variable.
-func writeExpression(buf *bytes.Buffer, v *cd.Variable, printName bool, varTable []*cd.Variable, seen map[*cd.Variable]bool) {
- if v == nil {
- // Shouldn't happen.
- return
- }
- name, value, status, members := v.Name, v.Value, v.Status, v.Members
-
- // If v.VarTableIndex is not zero, it refers to an element of varTable.
- // We merge its fields with the fields we got from v.
- var other *cd.Variable
- if idx := int(v.VarTableIndex); idx > 0 && idx < len(varTable) {
- other = varTable[idx]
- }
- if other != nil {
- if name == "" {
- name = other.Name
- }
- if value == "" {
- value = other.Value
- }
- if status == nil {
- status = other.Status
- }
- if len(members) == 0 {
- members = other.Members
- }
- }
- if printName && name != "" {
- buf.WriteString(name)
- buf.WriteByte(':')
- }
-
- // If we have seen this value before, write "..." rather than repeating it.
- if seen[v] {
- buf.WriteString("...")
- return
- }
- seen[v] = true
- if other != nil {
- if seen[other] {
- buf.WriteString("...")
- return
- }
- seen[other] = true
- }
-
- if value != "" && !strings.HasPrefix(value, "len = ") {
- // A plain value.
- buf.WriteString(value)
- } else if status != nil && status.Description != nil {
- // An error.
- for _, p := range status.Description.Parameters {
- buf.WriteByte('(')
- buf.WriteString(p)
- buf.WriteByte(')')
- }
- } else if name == `⚫` {
- // A map element.
- first := true
- for _, member := range members {
- if first {
- first = false
- } else {
- buf.WriteByte(':')
- }
- writeExpression(buf, member, false, varTable, seen)
- }
- } else {
- // A map, array, slice, channel, or struct.
- isStruct := value == ""
- first := true
- buf.WriteByte('{')
- for _, member := range members {
- if first {
- first = false
- } else {
- buf.WriteString(", ")
- }
- writeExpression(buf, member, isStruct, varTable, seen)
- }
- buf.WriteByte('}')
- }
-}
-
-const (
- // Error messages for cd.StatusMessage
- messageNotCaptured = "Not captured"
- messageTruncated = "Truncated"
- messageUnknownPointerType = "Unknown pointer type"
- messageUnknownType = "Unknown type"
- // RefersTo values for cd.StatusMessage.
- refersToVariableName = iota
- refersToVariableValue
-)
-
-// refersToString contains the strings for each refersTo value.
-// See the definition of StatusMessage in the v2/clouddebugger package.
-var refersToString = map[int]string{
- refersToVariableName: "VARIABLE_NAME",
- refersToVariableValue: "VARIABLE_VALUE",
-}
diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector_test.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector_test.go
deleted file mode 100644
index 2bc97dcfa..000000000
--- a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/valuecollector/valuecollector_test.go
+++ /dev/null
@@ -1,418 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// 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 valuecollector
-
-import (
- "fmt"
- "reflect"
- "testing"
-
- "golang.org/x/debug"
- cd "google.golang.org/api/clouddebugger/v2"
-)
-
-const (
- // Some arbitrary type IDs for the test, for use in debug.Var's TypeID field.
- // A TypeID of 0 means the type is unknown, so we start at 1.
- int16Type = iota + 1
- stringType
- structType
- pointerType
- arrayType
- int32Type
- debugStringType
- mapType
- channelType
- sliceType
-)
-
-func TestValueCollector(t *testing.T) {
- // Construct the collector.
- c := NewCollector(&Program{}, 26)
- // Add some variables of various types, whose values we want the collector to read.
- variablesToAdd := []debug.LocalVar{
- {Name: "a", Var: debug.Var{int16Type, 0x1}},
- {Name: "b", Var: debug.Var{stringType, 0x2}},
- {Name: "c", Var: debug.Var{structType, 0x3}},
- {Name: "d", Var: debug.Var{pointerType, 0x4}},
- {Name: "e", Var: debug.Var{arrayType, 0x5}},
- {Name: "f", Var: debug.Var{debugStringType, 0x6}},
- {Name: "g", Var: debug.Var{mapType, 0x7}},
- {Name: "h", Var: debug.Var{channelType, 0x8}},
- {Name: "i", Var: debug.Var{sliceType, 0x9}},
- }
- expectedResults := []*cd.Variable{
- &cd.Variable{Name: "a", VarTableIndex: 1},
- &cd.Variable{Name: "b", VarTableIndex: 2},
- &cd.Variable{Name: "c", VarTableIndex: 3},
- &cd.Variable{Name: "d", VarTableIndex: 4},
- &cd.Variable{Name: "e", VarTableIndex: 5},
- &cd.Variable{Name: "f", VarTableIndex: 6},
- &cd.Variable{Name: "g", VarTableIndex: 7},
- &cd.Variable{Name: "h", VarTableIndex: 8},
- &cd.Variable{Name: "i", VarTableIndex: 9},
- }
- for i, v := range variablesToAdd {
- added := c.AddVariable(v)
- if !reflect.DeepEqual(added, expectedResults[i]) {
- t.Errorf("AddVariable: got %+v want %+v", *added, *expectedResults[i])
- }
- }
- // Read the values, compare the output to what we expect.
- v := c.ReadValues()
- expectedValues := []*cd.Variable{
- &cd.Variable{},
- &cd.Variable{Value: "1"},
- &cd.Variable{Value: `"hello"`},
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "x", VarTableIndex: 1},
- &cd.Variable{Name: "y", VarTableIndex: 2},
- },
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{VarTableIndex: 1},
- },
- Value: "0x1",
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "[0]", VarTableIndex: 10},
- &cd.Variable{Name: "[1]", VarTableIndex: 11},
- &cd.Variable{Name: "[2]", VarTableIndex: 12},
- &cd.Variable{Name: "[3]", VarTableIndex: 13},
- },
- Value: "len = 4",
- },
- &cd.Variable{Value: `"world"`},
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "⚫", VarTableIndex: 14},
- &cd.Variable{Name: "⚫", VarTableIndex: 15},
- &cd.Variable{Name: "⚫", VarTableIndex: 16},
- },
- Value: "len = 3",
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "[0]", VarTableIndex: 17},
- &cd.Variable{Name: "[1]", VarTableIndex: 18},
- },
- Value: "len = 2",
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "[0]", VarTableIndex: 19},
- &cd.Variable{Name: "[1]", VarTableIndex: 20},
- },
- Value: "len = 2",
- },
- &cd.Variable{Value: "100"},
- &cd.Variable{Value: "104"},
- &cd.Variable{Value: "108"},
- &cd.Variable{Value: "112"},
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "key", VarTableIndex: 21},
- &cd.Variable{Name: "value", VarTableIndex: 22},
- },
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "key", VarTableIndex: 23},
- &cd.Variable{Name: "value", VarTableIndex: 24},
- },
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "key", VarTableIndex: 25},
- &cd.Variable{
- Name: "value",
- Status: &cd.StatusMessage{
- Description: &cd.FormatMessage{
- Format: "$0",
- Parameters: []string{"Not captured"},
- },
- IsError: true,
- RefersTo: "VARIABLE_NAME",
- },
- },
- },
- },
- &cd.Variable{Value: "246"},
- &cd.Variable{Value: "210"},
- &cd.Variable{Value: "300"},
- &cd.Variable{Value: "304"},
- &cd.Variable{Value: "400"},
- &cd.Variable{Value: "404"},
- &cd.Variable{Value: "1400"},
- &cd.Variable{Value: "1404"},
- &cd.Variable{Value: "2400"},
- }
- if !reflect.DeepEqual(v, expectedValues) {
- t.Errorf("ReadValues: got %v want %v", v, expectedValues)
- // Do element-by-element comparisons, for more useful error messages.
- for i := range v {
- if i < len(expectedValues) && !reflect.DeepEqual(v[i], expectedValues[i]) {
- t.Errorf("element %d: got %+v want %+v", i, *v[i], *expectedValues[i])
- }
- }
- }
-}
-
-// Program implements the similarly-named interface in x/debug.
-// ValueCollector should only call its Value and MapElement methods.
-type Program struct {
- debug.Program
-}
-
-func (p *Program) Value(v debug.Var) (debug.Value, error) {
- // We determine what to return using v.TypeID.
- switch v.TypeID {
- case int16Type:
- // We use the address as the value, so that we're testing whether the right
- // address was calculated.
- return int16(v.Address), nil
- case stringType:
- // A string.
- return "hello", nil
- case structType:
- // A struct with two elements.
- return debug.Struct{
- Fields: []debug.StructField{
- {
- Name: "x",
- Var: debug.Var{int16Type, 0x1},
- },
- {
- Name: "y",
- Var: debug.Var{stringType, 0x2},
- },
- },
- }, nil
- case pointerType:
- // A pointer to the first variable above.
- return debug.Pointer{int16Type, 0x1}, nil
- case arrayType:
- // An array of 4 32-bit-wide elements.
- return debug.Array{
- ElementTypeID: int32Type,
- Address: 0x64,
- Length: 4,
- StrideBits: 32,
- }, nil
- case debugStringType:
- return debug.String{
- Length: 5,
- String: "world",
- }, nil
- case mapType:
- return debug.Map{
- TypeID: 99,
- Address: 0x100,
- Length: 3,
- }, nil
- case channelType:
- return debug.Channel{
- ElementTypeID: int32Type,
- Address: 200,
- Buffer: 210,
- Length: 2,
- Capacity: 10,
- Stride: 4,
- BufferStart: 9,
- }, nil
- case sliceType:
- // A slice of 2 32-bit-wide elements.
- return debug.Slice{
- Array: debug.Array{
- ElementTypeID: int32Type,
- Address: 300,
- Length: 2,
- StrideBits: 32,
- },
- Capacity: 50,
- }, nil
- case int32Type:
- // We use the address as the value, so that we're testing whether the right
- // address was calculated.
- return int32(v.Address), nil
- }
- return nil, fmt.Errorf("unexpected Value request")
-}
-
-func (p *Program) MapElement(m debug.Map, index uint64) (debug.Var, debug.Var, error) {
- return debug.Var{TypeID: int16Type, Address: 1000*index + 400},
- debug.Var{TypeID: int32Type, Address: 1000*index + 404},
- nil
-}
-
-func TestLogString(t *testing.T) {
- bp := cd.Breakpoint{
- Action: "LOG",
- LogMessageFormat: "$0 hello, $$7world! $1 $2 $3 $4 $5$6 $7 $8",
- EvaluatedExpressions: []*cd.Variable{
- &cd.Variable{Name: "a", VarTableIndex: 1},
- &cd.Variable{Name: "b", VarTableIndex: 2},
- &cd.Variable{Name: "c", VarTableIndex: 3},
- &cd.Variable{Name: "d", VarTableIndex: 4},
- &cd.Variable{Name: "e", VarTableIndex: 5},
- &cd.Variable{Name: "f", VarTableIndex: 6},
- &cd.Variable{Name: "g", VarTableIndex: 7},
- &cd.Variable{Name: "h", VarTableIndex: 8},
- &cd.Variable{Name: "i", VarTableIndex: 9},
- },
- }
- varTable := []*cd.Variable{
- &cd.Variable{},
- &cd.Variable{Value: "1"},
- &cd.Variable{Value: `"hello"`},
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "x", Value: "1"},
- &cd.Variable{Name: "y", Value: `"hello"`},
- &cd.Variable{Name: "z", VarTableIndex: 3},
- },
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{VarTableIndex: 1},
- },
- Value: "0x1",
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "[0]", VarTableIndex: 10},
- &cd.Variable{Name: "[1]", VarTableIndex: 11},
- &cd.Variable{Name: "[2]", VarTableIndex: 12},
- &cd.Variable{Name: "[3]", VarTableIndex: 13},
- },
- Value: "len = 4",
- },
- &cd.Variable{Value: `"world"`},
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "⚫", VarTableIndex: 14},
- &cd.Variable{Name: "⚫", VarTableIndex: 15},
- &cd.Variable{Name: "⚫", VarTableIndex: 16},
- },
- Value: "len = 3",
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "[0]", VarTableIndex: 17},
- &cd.Variable{Name: "[1]", VarTableIndex: 18},
- },
- Value: "len = 2",
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "[0]", VarTableIndex: 19},
- &cd.Variable{Name: "[1]", VarTableIndex: 20},
- },
- Value: "len = 2",
- },
- &cd.Variable{Value: "100"},
- &cd.Variable{Value: "104"},
- &cd.Variable{Value: "108"},
- &cd.Variable{Value: "112"},
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "key", VarTableIndex: 21},
- &cd.Variable{Name: "value", VarTableIndex: 22},
- },
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "key", VarTableIndex: 23},
- &cd.Variable{Name: "value", VarTableIndex: 24},
- },
- },
- &cd.Variable{
- Members: []*cd.Variable{
- &cd.Variable{Name: "key", VarTableIndex: 25},
- &cd.Variable{
- Name: "value",
- Status: &cd.StatusMessage{
- Description: &cd.FormatMessage{
- Format: "$0",
- Parameters: []string{"Not captured"},
- },
- IsError: true,
- RefersTo: "VARIABLE_NAME",
- },
- },
- },
- },
- &cd.Variable{Value: "246"},
- &cd.Variable{Value: "210"},
- &cd.Variable{Value: "300"},
- &cd.Variable{Value: "304"},
- &cd.Variable{Value: "400"},
- &cd.Variable{Value: "404"},
- &cd.Variable{Value: "1400"},
- &cd.Variable{Value: "1404"},
- &cd.Variable{Value: "2400"},
- }
- s := LogString(bp.LogMessageFormat, bp.EvaluatedExpressions, varTable)
- expected := `LOGPOINT: 1 hello, $7world! "hello" {x:1, y:"hello", z:...} ` +
- `0x1 {100, 104, 108, 112} "world"{400:404, 1400:1404, 2400:(Not captured)} ` +
- `{246, 210} {300, 304}`
- if s != expected {
- t.Errorf("LogString: got %q want %q", s, expected)
- }
-}
-
-func TestParseToken(t *testing.T) {
- for _, c := range []struct {
- s string
- max int
- num int
- n int
- ok bool
- }{
- {"", 0, 0, 0, false},
- {".", 0, 0, 0, false},
- {"0", 0, 0, 1, true},
- {"0", 1, 0, 1, true},
- {"00", 0, 0, 2, true},
- {"1.", 1, 1, 1, true},
- {"1.", 0, 0, 0, false},
- {"10", 10, 10, 2, true},
- {"10..", 10, 10, 2, true},
- {"10", 11, 10, 2, true},
- {"10..", 11, 10, 2, true},
- {"10", 9, 0, 0, false},
- {"10..", 9, 0, 0, false},
- {" 10", 10, 0, 0, false},
- {"010", 10, 10, 3, true},
- {"123456789", 123456789, 123456789, 9, true},
- {"123456789", 123456788, 0, 0, false},
- {"123456789123456789123456789", 999999999, 0, 0, false},
- } {
- num, n, ok := parseToken(c.s, c.max)
- if ok != c.ok {
- t.Errorf("parseToken(%q, %d): got ok=%t want ok=%t", c.s, c.max, ok, c.ok)
- continue
- }
- if !ok {
- continue
- }
- if num != c.num || n != c.n {
- t.Errorf("parseToken(%q, %d): got %d,%d,%t want %d,%d,%t", c.s, c.max, num, n, ok, c.num, c.n, c.ok)
- }
- }
-}