mirror of
https://github.com/restic/restic.git
synced 2025-10-27 12:38:33 +00:00
Vendor dependencies for GCS
This commit is contained in:
174
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints.go
generated
vendored
Normal file
174
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
// 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
|
||||
}
|
||||
168
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints_test.go
generated
vendored
Normal file
168
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/breakpoints/breakpoints_test.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user