terminal: add transcript command (#2814)
Adds a transcript command that appends all command output to a file. This command is equivalent to gdb's 'set logging'. As part of this refactor the pkg/terminal commands to always write to a io.Writer instead of using os.Stdout directly (through fmt.Printf/fmt.Println). Fixes #2237
This commit is contained in:
parent
c3eb1cf828
commit
5b925d4f5d
@ -91,6 +91,7 @@ Command | Description
|
|||||||
[list](#list) | Show source code.
|
[list](#list) | Show source code.
|
||||||
[source](#source) | Executes a file containing a list of delve commands
|
[source](#source) | Executes a file containing a list of delve commands
|
||||||
[sources](#sources) | Print list of source files.
|
[sources](#sources) | Print list of source files.
|
||||||
|
[transcript](#transcript) | Appends command output to a file.
|
||||||
[types](#types) | Print list of types
|
[types](#types) | Print list of types
|
||||||
|
|
||||||
## args
|
## args
|
||||||
@ -636,6 +637,17 @@ See also: "help on", "help cond" and "help clear"
|
|||||||
|
|
||||||
Aliases: t
|
Aliases: t
|
||||||
|
|
||||||
|
## transcript
|
||||||
|
Appends command output to a file.
|
||||||
|
|
||||||
|
transcript [-t] [-x] <output file>
|
||||||
|
transcript -off
|
||||||
|
|
||||||
|
Output of Delve's command is appended to the specified output file. If '-t' is specified and the output file exists it is truncated. If '-x' is specified output to stdout is suppressed instead.
|
||||||
|
|
||||||
|
Using the -off option disables the transcript.
|
||||||
|
|
||||||
|
|
||||||
## types
|
## types
|
||||||
Print list of types
|
Print list of types
|
||||||
|
|
||||||
|
@ -670,6 +670,7 @@ func traceCmd(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
cmds := terminal.DebugCommands(client)
|
cmds := terminal.DebugCommands(client)
|
||||||
t := terminal.New(client, nil)
|
t := terminal.New(client, nil)
|
||||||
|
t.RedirectTo(os.Stderr)
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
if traceUseEBPF {
|
if traceUseEBPF {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
@ -962,7 +962,7 @@ func TestTracePid(t *testing.T) {
|
|||||||
dlvbin, tmpdir := getDlvBin(t)
|
dlvbin, tmpdir := getDlvBin(t)
|
||||||
defer os.RemoveAll(tmpdir)
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
expected := []byte("goroutine(1): main.A() => ()\n")
|
expected := []byte("goroutine(1): main.A()\n => ()\n")
|
||||||
|
|
||||||
// make process run
|
// make process run
|
||||||
fix := protest.BuildFixture("issue2023", 0)
|
fix := protest.BuildFixture("issue2023", 0)
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
"github.com/cosiner/argv"
|
"github.com/cosiner/argv"
|
||||||
"github.com/go-delve/delve/pkg/config"
|
"github.com/go-delve/delve/pkg/config"
|
||||||
"github.com/go-delve/delve/pkg/locspec"
|
"github.com/go-delve/delve/pkg/locspec"
|
||||||
"github.com/go-delve/delve/pkg/terminal/colorize"
|
|
||||||
"github.com/go-delve/delve/service"
|
"github.com/go-delve/delve/service"
|
||||||
"github.com/go-delve/delve/service/api"
|
"github.com/go-delve/delve/service/api"
|
||||||
"github.com/go-delve/delve/service/rpc2"
|
"github.com/go-delve/delve/service/rpc2"
|
||||||
@ -536,6 +535,15 @@ If display is called without arguments it will print the value of all expression
|
|||||||
dump <output file>
|
dump <output file>
|
||||||
|
|
||||||
The core dump is always written in ELF, even on systems (windows, macOS) where this is not customary. For environments other than linux/amd64 threads and registers are dumped in a format that only Delve can read back.`},
|
The core dump is always written in ELF, even on systems (windows, macOS) where this is not customary. For environments other than linux/amd64 threads and registers are dumped in a format that only Delve can read back.`},
|
||||||
|
|
||||||
|
{aliases: []string{"transcript"}, cmdFn: transcript, helpMsg: `Appends command output to a file.
|
||||||
|
|
||||||
|
transcript [-t] [-x] <output file>
|
||||||
|
transcript -off
|
||||||
|
|
||||||
|
Output of Delve's command is appended to the specified output file. If '-t' is specified and the output file exists it is truncated. If '-x' is specified output to stdout is suppressed instead.
|
||||||
|
|
||||||
|
Using the -off option disables the transcript.`},
|
||||||
}
|
}
|
||||||
|
|
||||||
addrecorded := client == nil
|
addrecorded := client == nil
|
||||||
@ -674,7 +682,7 @@ func (c *Commands) help(t *Term, ctx callContext, args string) error {
|
|||||||
for _, cmd := range c.cmds {
|
for _, cmd := range c.cmds {
|
||||||
for _, alias := range cmd.aliases {
|
for _, alias := range cmd.aliases {
|
||||||
if alias == args {
|
if alias == args {
|
||||||
fmt.Println(cmd.helpMsg)
|
fmt.Fprintln(t.stdout, cmd.helpMsg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -682,12 +690,12 @@ func (c *Commands) help(t *Term, ctx callContext, args string) error {
|
|||||||
return errNoCmd
|
return errNoCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("The following commands are available:")
|
fmt.Fprintln(t.stdout, "The following commands are available:")
|
||||||
|
|
||||||
for _, cgd := range commandGroupDescriptions {
|
for _, cgd := range commandGroupDescriptions {
|
||||||
fmt.Printf("\n%s:\n", cgd.description)
|
fmt.Fprintf(t.stdout, "\n%s:\n", cgd.description)
|
||||||
w := new(tabwriter.Writer)
|
w := new(tabwriter.Writer)
|
||||||
w.Init(os.Stdout, 0, 8, 0, '-', 0)
|
w.Init(t.stdout, 0, 8, 0, '-', 0)
|
||||||
for _, cmd := range c.cmds {
|
for _, cmd := range c.cmds {
|
||||||
if cmd.group != cgd.group {
|
if cmd.group != cgd.group {
|
||||||
continue
|
continue
|
||||||
@ -707,8 +715,8 @@ func (c *Commands) help(t *Term, ctx callContext, args string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Fprintln(t.stdout)
|
||||||
fmt.Println("Type help followed by a command for full documentation.")
|
fmt.Fprintln(t.stdout, "Type help followed by a command for full documentation.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -734,11 +742,11 @@ func threads(t *Term, ctx callContext, args string) error {
|
|||||||
prefix = "* "
|
prefix = "* "
|
||||||
}
|
}
|
||||||
if th.Function != nil {
|
if th.Function != nil {
|
||||||
fmt.Printf("%sThread %d at %#v %s:%d %s\n",
|
fmt.Fprintf(t.stdout, "%sThread %d at %#v %s:%d %s\n",
|
||||||
prefix, th.ID, th.PC, t.formatPath(th.File),
|
prefix, th.ID, th.PC, t.formatPath(th.File),
|
||||||
th.Line, th.Function.Name())
|
th.Line, th.Function.Name())
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%sThread %s\n", prefix, t.formatThread(th))
|
fmt.Fprintf(t.stdout, "%sThread %s\n", prefix, t.formatThread(th))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -769,7 +777,7 @@ func thread(t *Term, ctx callContext, args string) error {
|
|||||||
if newState.CurrentThread != nil {
|
if newState.CurrentThread != nil {
|
||||||
newThread = strconv.Itoa(newState.CurrentThread.ID)
|
newThread = strconv.Itoa(newState.CurrentThread.ID)
|
||||||
}
|
}
|
||||||
fmt.Printf("Switched from %s to %s\n", oldThread, newThread)
|
fmt.Fprintf(t.stdout, "Switched from %s to %s\n", oldThread, newThread)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,16 +793,16 @@ func printGoroutines(t *Term, indent string, gs []*api.Goroutine, fgl api.Format
|
|||||||
if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID {
|
if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID {
|
||||||
prefix = indent + "* "
|
prefix = indent + "* "
|
||||||
}
|
}
|
||||||
fmt.Printf("%sGoroutine %s\n", prefix, t.formatGoroutine(g, fgl))
|
fmt.Fprintf(t.stdout, "%sGoroutine %s\n", prefix, t.formatGoroutine(g, fgl))
|
||||||
if flags&api.PrintGoroutinesLabels != 0 {
|
if flags&api.PrintGoroutinesLabels != 0 {
|
||||||
writeGoroutineLabels(os.Stdout, g, indent+"\t")
|
writeGoroutineLabels(t.stdout, g, indent+"\t")
|
||||||
}
|
}
|
||||||
if flags&api.PrintGoroutinesStack != 0 {
|
if flags&api.PrintGoroutinesStack != 0 {
|
||||||
stack, err := t.client.Stacktrace(g.ID, depth, 0, nil)
|
stack, err := t.client.Stacktrace(g.ID, depth, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
printStack(t, os.Stdout, stack, indent+"\t", false)
|
printStack(t, t.stdout, stack, indent+"\t", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -820,7 +828,7 @@ func goroutines(t *Term, ctx callContext, argstr string) error {
|
|||||||
t.longCommandStart()
|
t.longCommandStart()
|
||||||
for start >= 0 {
|
for start >= 0 {
|
||||||
if t.longCommandCanceled() {
|
if t.longCommandCanceled() {
|
||||||
fmt.Printf("interrupted\n")
|
fmt.Fprintf(t.stdout, "interrupted\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
gs, groups, start, tooManyGroups, err = t.client.ListGoroutinesWithFilter(start, batchSize, filters, &group)
|
gs, groups, start, tooManyGroups, err = t.client.ListGoroutinesWithFilter(start, batchSize, filters, &group)
|
||||||
@ -829,18 +837,18 @@ func goroutines(t *Term, ctx callContext, argstr string) error {
|
|||||||
}
|
}
|
||||||
if len(groups) > 0 {
|
if len(groups) > 0 {
|
||||||
for i := range groups {
|
for i := range groups {
|
||||||
fmt.Printf("%s\n", groups[i].Name)
|
fmt.Fprintf(t.stdout, "%s\n", groups[i].Name)
|
||||||
err = printGoroutines(t, "\t", gs[groups[i].Offset:][:groups[i].Count], fgl, flags, depth, state)
|
err = printGoroutines(t, "\t", gs[groups[i].Offset:][:groups[i].Count], fgl, flags, depth, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("\tTotal: %d\n", groups[i].Total)
|
fmt.Fprintf(t.stdout, "\tTotal: %d\n", groups[i].Total)
|
||||||
if i != len(groups)-1 {
|
if i != len(groups)-1 {
|
||||||
fmt.Printf("\n")
|
fmt.Fprintf(t.stdout, "\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tooManyGroups {
|
if tooManyGroups {
|
||||||
fmt.Printf("Too many groups\n")
|
fmt.Fprintf(t.stdout, "Too many groups\n")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sort.Sort(byGoroutineID(gs))
|
sort.Sort(byGoroutineID(gs))
|
||||||
@ -852,7 +860,7 @@ func goroutines(t *Term, ctx callContext, argstr string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if gslen > 0 {
|
if gslen > 0 {
|
||||||
fmt.Printf("[%d goroutines]\n", gslen)
|
fmt.Fprintf(t.stdout, "[%d goroutines]\n", gslen)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -893,7 +901,7 @@ func (c *Commands) goroutine(t *Term, ctx callContext, argstr string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.frame = 0
|
c.frame = 0
|
||||||
fmt.Printf("Switched from %d to %d (thread %d)\n", selectedGID(oldState), gid, newState.CurrentThread.ID)
|
fmt.Fprintf(t.stdout, "Switched from %d to %d (thread %d)\n", selectedGID(oldState), gid, newState.CurrentThread.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -950,7 +958,7 @@ func (c *Commands) frameCommand(t *Term, ctx callContext, argstr string, directi
|
|||||||
}
|
}
|
||||||
printcontext(t, state)
|
printcontext(t, state)
|
||||||
th := stack[frame]
|
th := stack[frame]
|
||||||
fmt.Printf("Frame %d: %s:%d (PC: %x)\n", frame, t.formatPath(th.File), th.Line, th.PC)
|
fmt.Fprintf(t.stdout, "Frame %d: %s:%d (PC: %x)\n", frame, t.formatPath(th.File), th.Line, th.PC)
|
||||||
printfile(t, th.File, th.Line, true)
|
printfile(t, th.File, th.Line, true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -980,9 +988,9 @@ func printscope(t *Term) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Thread %s\n", t.formatThread(state.CurrentThread))
|
fmt.Fprintf(t.stdout, "Thread %s\n", t.formatThread(state.CurrentThread))
|
||||||
if state.SelectedGoroutine != nil {
|
if state.SelectedGoroutine != nil {
|
||||||
writeGoroutineLong(t, os.Stdout, state.SelectedGoroutine, "")
|
writeGoroutineLong(t, t.stdout, state.SelectedGoroutine, "")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1182,7 +1190,7 @@ func restartLive(t *Term, ctx callContext, args string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Process restarted with PID", t.client.ProcessPid())
|
fmt.Fprintln(t.stdout, "Process restarted with PID", t.client.ProcessPid())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1192,7 +1200,7 @@ func restartIntl(t *Term, rerecord bool, restartPos string, resetArgs bool, newA
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := range discarded {
|
for i := range discarded {
|
||||||
fmt.Printf("Discarded %s at %s: %v\n", formatBreakpointName(discarded[i].Breakpoint, false), t.formatBreakpointLocation(discarded[i].Breakpoint), discarded[i].Reason)
|
fmt.Fprintf(t.stdout, "Discarded %s at %s: %v\n", formatBreakpointName(discarded[i].Breakpoint, false), t.formatBreakpointLocation(discarded[i].Breakpoint), discarded[i].Reason)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1276,7 +1284,7 @@ func (c *Commands) rebuild(t *Term, ctx callContext, args string) error {
|
|||||||
defer t.onStop()
|
defer t.onStop()
|
||||||
discarded, err := t.client.Restart(true)
|
discarded, err := t.client.Restart(true)
|
||||||
if len(discarded) > 0 {
|
if len(discarded) > 0 {
|
||||||
fmt.Printf("not all breakpoints could be restored.")
|
fmt.Fprintf(t.stdout, "not all breakpoints could be restored.")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1292,7 +1300,7 @@ func (c *Commands) cont(t *Term, ctx callContext, args string) error {
|
|||||||
defer func() {
|
defer func() {
|
||||||
for _, bp := range tmp {
|
for _, bp := range tmp {
|
||||||
if _, err := t.client.ClearBreakpoint(bp.ID); err != nil {
|
if _, err := t.client.ClearBreakpoint(bp.ID); err != nil {
|
||||||
fmt.Printf("failed to clear temporary breakpoint: %d", bp.ID)
|
fmt.Fprintf(t.stdout, "failed to clear temporary breakpoint: %d", bp.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -1325,16 +1333,16 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho
|
|||||||
}
|
}
|
||||||
skipBreakpoints := false
|
skipBreakpoints := false
|
||||||
for {
|
for {
|
||||||
fmt.Printf("\tbreakpoint hit during %s", op)
|
fmt.Fprintf(t.stdout, "\tbreakpoint hit during %s", op)
|
||||||
if !skipBreakpoints {
|
if !skipBreakpoints {
|
||||||
fmt.Printf("\n")
|
fmt.Fprintf(t.stdout, "\n")
|
||||||
answer, err := promptAutoContinue(t, op)
|
answer, err := promptAutoContinue(t, op)
|
||||||
switch answer {
|
switch answer {
|
||||||
case "f": // finish next
|
case "f": // finish next
|
||||||
skipBreakpoints = true
|
skipBreakpoints = true
|
||||||
fallthrough
|
fallthrough
|
||||||
case "c": // continue once
|
case "c": // continue once
|
||||||
fmt.Printf("continuing...\n")
|
fmt.Fprintf(t.stdout, "continuing...\n")
|
||||||
case "s": // stop and cancel
|
case "s": // stop and cancel
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
@ -1343,7 +1351,7 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(", continuing...\n")
|
fmt.Fprintf(t.stdout, ", continuing...\n")
|
||||||
}
|
}
|
||||||
stateChan := t.client.DirectionCongruentContinue()
|
stateChan := t.client.DirectionCongruentContinue()
|
||||||
var state *api.DebuggerState
|
var state *api.DebuggerState
|
||||||
@ -1542,7 +1550,7 @@ func clear(t *Term, ctx callContext, args string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("%s cleared at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
fmt.Fprintf(t.stdout, "%s cleared at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1580,9 +1588,9 @@ func clearAll(t *Term, ctx callContext, args string) error {
|
|||||||
|
|
||||||
_, err := t.client.ClearBreakpoint(bp.ID)
|
_, err := t.client.ClearBreakpoint(bp.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Couldn't delete %s at %s: %s\n", formatBreakpointName(bp, false), t.formatBreakpointLocation(bp), err)
|
fmt.Fprintf(t.stdout, "Couldn't delete %s at %s: %s\n", formatBreakpointName(bp, false), t.formatBreakpointLocation(bp), err)
|
||||||
}
|
}
|
||||||
fmt.Printf("%s cleared at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
fmt.Fprintf(t.stdout, "%s cleared at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1601,7 +1609,7 @@ func toggle(t *Term, ctx callContext, args string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("%s toggled at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
fmt.Fprintf(t.stdout, "%s toggled at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1623,12 +1631,12 @@ func breakpoints(t *Term, ctx callContext, args string) error {
|
|||||||
if bp.Disabled {
|
if bp.Disabled {
|
||||||
enabled = "(disabled)"
|
enabled = "(disabled)"
|
||||||
}
|
}
|
||||||
fmt.Printf("%s %s at %v (%d)\n", formatBreakpointName(bp, true), enabled, t.formatBreakpointLocation(bp), bp.TotalHitCount)
|
fmt.Fprintf(t.stdout, "%s %s at %v (%d)\n", formatBreakpointName(bp, true), enabled, t.formatBreakpointLocation(bp), bp.TotalHitCount)
|
||||||
|
|
||||||
attrs := formatBreakpointAttrs("\t", bp, false)
|
attrs := formatBreakpointAttrs("\t", bp, false)
|
||||||
|
|
||||||
if len(attrs) > 0 {
|
if len(attrs) > 0 {
|
||||||
fmt.Printf("%s\n", strings.Join(attrs, "\n"))
|
fmt.Fprintf(t.stdout, "%s\n", strings.Join(attrs, "\n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -1726,7 +1734,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) ([]
|
|||||||
}
|
}
|
||||||
created = append(created, bp)
|
created = append(created, bp)
|
||||||
|
|
||||||
fmt.Printf("%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
fmt.Fprintf(t.stdout, "%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
||||||
}
|
}
|
||||||
|
|
||||||
var shouldSetReturnBreakpoints bool
|
var shouldSetReturnBreakpoints bool
|
||||||
@ -1825,7 +1833,7 @@ func watchpoint(t *Term, ctx callContext, args string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
fmt.Fprintf(t.stdout, "%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1955,7 +1963,7 @@ loop:
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Print(api.PrettyExamineMemory(uintptr(address), memArea, isLittleEndian, priFmt, size))
|
fmt.Fprint(t.stdout, api.PrettyExamineMemory(uintptr(address), memArea, isLittleEndian, priFmt, size))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1984,7 +1992,7 @@ func printVar(t *Term, ctx callContext, args string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(val.MultilineString("", fmtstr))
|
fmt.Fprintln(t.stdout, val.MultilineString("", fmtstr))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1997,20 +2005,20 @@ func whatisCommand(t *Term, ctx callContext, args string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if val.Flags&api.VariableCPURegister != 0 {
|
if val.Flags&api.VariableCPURegister != 0 {
|
||||||
fmt.Println("CPU Register")
|
fmt.Fprintln(t.stdout, "CPU Register")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if val.Type != "" {
|
if val.Type != "" {
|
||||||
fmt.Println(val.Type)
|
fmt.Fprintln(t.stdout, val.Type)
|
||||||
}
|
}
|
||||||
if val.RealType != val.Type {
|
if val.RealType != val.Type {
|
||||||
fmt.Printf("Real type: %s\n", val.RealType)
|
fmt.Fprintf(t.stdout, "Real type: %s\n", val.RealType)
|
||||||
}
|
}
|
||||||
if val.Kind == reflect.Interface && len(val.Children) > 0 {
|
if val.Kind == reflect.Interface && len(val.Children) > 0 {
|
||||||
fmt.Printf("Concrete type: %s\n", val.Children[0].Type)
|
fmt.Fprintf(t.stdout, "Concrete type: %s\n", val.Children[0].Type)
|
||||||
}
|
}
|
||||||
if t.conf.ShowLocationExpr && val.LocationExpr != "" {
|
if t.conf.ShowLocationExpr && val.LocationExpr != "" {
|
||||||
fmt.Printf("location: %s\n", val.LocationExpr)
|
fmt.Fprintf(t.stdout, "location: %s\n", val.LocationExpr)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2032,7 +2040,7 @@ func setVar(t *Term, ctx callContext, args string) error {
|
|||||||
return t.client.SetVariable(ctx.Scope, lexpr, rexpr)
|
return t.client.SetVariable(ctx.Scope, lexpr, rexpr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printFilteredVariables(varType string, vars []api.Variable, filter string, cfg api.LoadConfig) error {
|
func (t *Term) printFilteredVariables(varType string, vars []api.Variable, filter string, cfg api.LoadConfig) error {
|
||||||
reg, err := regexp.Compile(filter)
|
reg, err := regexp.Compile(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -2046,39 +2054,39 @@ func printFilteredVariables(varType string, vars []api.Variable, filter string,
|
|||||||
name = "(" + name + ")"
|
name = "(" + name + ")"
|
||||||
}
|
}
|
||||||
if cfg == ShortLoadConfig {
|
if cfg == ShortLoadConfig {
|
||||||
fmt.Printf("%s = %s\n", name, v.SinglelineString())
|
fmt.Fprintf(t.stdout, "%s = %s\n", name, v.SinglelineString())
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s = %s\n", name, v.MultilineString("", ""))
|
fmt.Fprintf(t.stdout, "%s = %s\n", name, v.MultilineString("", ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !match {
|
if !match {
|
||||||
fmt.Printf("(no %s)\n", varType)
|
fmt.Fprintf(t.stdout, "(no %s)\n", varType)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printSortedStrings(v []string, err error) error {
|
func (t *Term) printSortedStrings(v []string, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sort.Strings(v)
|
sort.Strings(v)
|
||||||
for _, d := range v {
|
for _, d := range v {
|
||||||
fmt.Println(d)
|
fmt.Fprintln(t.stdout, d)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sources(t *Term, ctx callContext, args string) error {
|
func sources(t *Term, ctx callContext, args string) error {
|
||||||
return printSortedStrings(t.client.ListSources(args))
|
return t.printSortedStrings(t.client.ListSources(args))
|
||||||
}
|
}
|
||||||
|
|
||||||
func funcs(t *Term, ctx callContext, args string) error {
|
func funcs(t *Term, ctx callContext, args string) error {
|
||||||
return printSortedStrings(t.client.ListFunctions(args))
|
return t.printSortedStrings(t.client.ListFunctions(args))
|
||||||
}
|
}
|
||||||
|
|
||||||
func types(t *Term, ctx callContext, args string) error {
|
func types(t *Term, ctx callContext, args string) error {
|
||||||
return printSortedStrings(t.client.ListTypes(args))
|
return t.printSortedStrings(t.client.ListTypes(args))
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseVarArguments(args string, t *Term) (filter string, cfg api.LoadConfig) {
|
func parseVarArguments(args string, t *Term) (filter string, cfg api.LoadConfig) {
|
||||||
@ -2105,7 +2113,7 @@ func args(t *Term, ctx callContext, args string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return printFilteredVariables("args", vars, filter, cfg)
|
return t.printFilteredVariables("args", vars, filter, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func locals(t *Term, ctx callContext, args string) error {
|
func locals(t *Term, ctx callContext, args string) error {
|
||||||
@ -2121,7 +2129,7 @@ func locals(t *Term, ctx callContext, args string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return printFilteredVariables("locals", locals, filter, cfg)
|
return t.printFilteredVariables("locals", locals, filter, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func vars(t *Term, ctx callContext, args string) error {
|
func vars(t *Term, ctx callContext, args string) error {
|
||||||
@ -2130,7 +2138,7 @@ func vars(t *Term, ctx callContext, args string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return printFilteredVariables("vars", vars, filter, cfg)
|
return t.printFilteredVariables("vars", vars, filter, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func regs(t *Term, ctx callContext, args string) error {
|
func regs(t *Term, ctx callContext, args string) error {
|
||||||
@ -2148,7 +2156,7 @@ func regs(t *Term, ctx callContext, args string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(regs)
|
fmt.Fprintln(t.stdout, regs)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2169,19 +2177,19 @@ func stackCommand(t *Term, ctx callContext, args string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
printStack(t, os.Stdout, stack, "", sa.offsets)
|
printStack(t, t.stdout, stack, "", sa.offsets)
|
||||||
if sa.ancestors > 0 {
|
if sa.ancestors > 0 {
|
||||||
ancestors, err := t.client.Ancestors(ctx.Scope.GoroutineID, sa.ancestors, sa.ancestorDepth)
|
ancestors, err := t.client.Ancestors(ctx.Scope.GoroutineID, sa.ancestors, sa.ancestorDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, ancestor := range ancestors {
|
for _, ancestor := range ancestors {
|
||||||
fmt.Printf("Created by Goroutine %d:\n", ancestor.ID)
|
fmt.Fprintf(t.stdout, "Created by Goroutine %d:\n", ancestor.ID)
|
||||||
if ancestor.Unreadable != "" {
|
if ancestor.Unreadable != "" {
|
||||||
fmt.Printf("\t%s\n", ancestor.Unreadable)
|
fmt.Fprintf(t.stdout, "\t%s\n", ancestor.Unreadable)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
printStack(t, os.Stdout, ancestor.Stack, "\t", false)
|
printStack(t, t.stdout, ancestor.Stack, "\t", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -2305,7 +2313,7 @@ func getLocation(t *Term, ctx callContext, args string, showContext bool) (file
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if showContext {
|
if showContext {
|
||||||
fmt.Printf("Goroutine %d frame %d at %s:%d (PC: %#x)\n", gid, ctx.Scope.Frame, loc.File, loc.Line, loc.PC)
|
fmt.Fprintf(t.stdout, "Goroutine %d frame %d at %s:%d (PC: %#x)\n", gid, ctx.Scope.Frame, loc.File, loc.Line, loc.PC)
|
||||||
}
|
}
|
||||||
return loc.File, loc.Line, true, nil
|
return loc.File, loc.Line, true, nil
|
||||||
|
|
||||||
@ -2319,7 +2327,7 @@ func getLocation(t *Term, ctx callContext, args string, showContext bool) (file
|
|||||||
}
|
}
|
||||||
loc := locs[0]
|
loc := locs[0]
|
||||||
if showContext {
|
if showContext {
|
||||||
fmt.Printf("Showing %s:%d (PC: %#x)\n", loc.File, loc.Line, loc.PC)
|
fmt.Fprintf(t.stdout, "Showing %s:%d (PC: %#x)\n", loc.File, loc.Line, loc.PC)
|
||||||
}
|
}
|
||||||
return loc.File, loc.Line, false, nil
|
return loc.File, loc.Line, false, nil
|
||||||
}
|
}
|
||||||
@ -2417,7 +2425,7 @@ func disassCommand(t *Term, ctx callContext, args string) error {
|
|||||||
return disasmErr
|
return disasmErr
|
||||||
}
|
}
|
||||||
|
|
||||||
disasmPrint(disasm, os.Stdout)
|
disasmPrint(disasm, t.stdout)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2429,7 +2437,7 @@ func libraries(t *Term, ctx callContext, args string) error {
|
|||||||
}
|
}
|
||||||
d := digits(len(libs))
|
d := digits(len(libs))
|
||||||
for i := range libs {
|
for i := range libs {
|
||||||
fmt.Printf("%"+strconv.Itoa(d)+"d. %#x %s\n", i, libs[i].Address, libs[i].Path)
|
fmt.Fprintf(t.stdout, "%"+strconv.Itoa(d)+"d. %#x %s\n", i, libs[i].Address, libs[i].Path)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2456,7 +2464,7 @@ func printcontext(t *Term, state *api.DebuggerState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if state.CurrentThread == nil {
|
if state.CurrentThread == nil {
|
||||||
fmt.Println("No current thread available")
|
fmt.Fprintln(t.stdout, "No current thread available")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2477,38 +2485,38 @@ func printcontext(t *Term, state *api.DebuggerState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if th.File == "" {
|
if th.File == "" {
|
||||||
fmt.Printf("Stopped at: 0x%x\n", state.CurrentThread.PC)
|
fmt.Fprintf(t.stdout, "Stopped at: 0x%x\n", state.CurrentThread.PC)
|
||||||
_ = colorize.Print(t.stdout, "", bytes.NewReader([]byte("no source available")), 1, 10, 1, nil)
|
t.stdout.ColorizePrint("", bytes.NewReader([]byte("no source available")), 1, 10, 1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
printcontextThread(t, th)
|
printcontextThread(t, th)
|
||||||
|
|
||||||
if state.When != "" {
|
if state.When != "" {
|
||||||
fmt.Println(state.When)
|
fmt.Fprintln(t.stdout, state.When)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, watchpoint := range state.WatchOutOfScope {
|
for _, watchpoint := range state.WatchOutOfScope {
|
||||||
fmt.Printf("%s went out of scope and was cleared\n", formatBreakpointName(watchpoint, true))
|
fmt.Fprintf(t.stdout, "%s went out of scope and was cleared\n", formatBreakpointName(watchpoint, true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printcontextLocation(t *Term, loc api.Location) {
|
func printcontextLocation(t *Term, loc api.Location) {
|
||||||
fmt.Printf("> %s() %s:%d (PC: %#v)\n", loc.Function.Name(), t.formatPath(loc.File), loc.Line, loc.PC)
|
fmt.Fprintf(t.stdout, "> %s() %s:%d (PC: %#v)\n", loc.Function.Name(), t.formatPath(loc.File), loc.Line, loc.PC)
|
||||||
if loc.Function != nil && loc.Function.Optimized {
|
if loc.Function != nil && loc.Function.Optimized {
|
||||||
fmt.Println(optimizedFunctionWarning)
|
fmt.Fprintln(t.stdout, optimizedFunctionWarning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printReturnValues(th *api.Thread) {
|
func printReturnValues(t *Term, th *api.Thread) {
|
||||||
if th.ReturnValues == nil {
|
if th.ReturnValues == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("Values returned:")
|
fmt.Fprintln(t.stdout, "Values returned:")
|
||||||
for _, v := range th.ReturnValues {
|
for _, v := range th.ReturnValues {
|
||||||
fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
|
fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Fprintln(t.stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printcontextThread(t *Term, th *api.Thread) {
|
func printcontextThread(t *Term, th *api.Thread) {
|
||||||
@ -2516,7 +2524,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
|||||||
|
|
||||||
if th.Breakpoint == nil {
|
if th.Breakpoint == nil {
|
||||||
printcontextLocation(t, api.Location{PC: th.PC, File: th.File, Line: th.Line, Function: th.Function})
|
printcontextLocation(t, api.Location{PC: th.PC, File: th.File, Line: th.Line, Function: th.Function})
|
||||||
printReturnValues(th)
|
printReturnValues(t, th)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2553,7 +2561,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hitCount, ok := th.Breakpoint.HitCount[strconv.Itoa(th.GoroutineID)]; ok {
|
if hitCount, ok := th.Breakpoint.HitCount[strconv.Itoa(th.GoroutineID)]; ok {
|
||||||
fmt.Printf("> %s%s(%s) %s:%d (hits goroutine(%d):%d total:%d) (PC: %#v)\n",
|
fmt.Fprintf(t.stdout, "> %s%s(%s) %s:%d (hits goroutine(%d):%d total:%d) (PC: %#v)\n",
|
||||||
bpname,
|
bpname,
|
||||||
fn.Name(),
|
fn.Name(),
|
||||||
args,
|
args,
|
||||||
@ -2564,7 +2572,7 @@ func printcontextThread(t *Term, th *api.Thread) {
|
|||||||
th.Breakpoint.TotalHitCount,
|
th.Breakpoint.TotalHitCount,
|
||||||
th.PC)
|
th.PC)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("> %s%s(%s) %s:%d (hits total:%d) (PC: %#v)\n",
|
fmt.Fprintf(t.stdout, "> %s%s(%s) %s:%d (hits total:%d) (PC: %#v)\n",
|
||||||
bpname,
|
bpname,
|
||||||
fn.Name(),
|
fn.Name(),
|
||||||
args,
|
args,
|
||||||
@ -2574,10 +2582,10 @@ func printcontextThread(t *Term, th *api.Thread) {
|
|||||||
th.PC)
|
th.PC)
|
||||||
}
|
}
|
||||||
if th.Function != nil && th.Function.Optimized {
|
if th.Function != nil && th.Function.Optimized {
|
||||||
fmt.Println(optimizedFunctionWarning)
|
fmt.Fprintln(t.stdout, optimizedFunctionWarning)
|
||||||
}
|
}
|
||||||
|
|
||||||
printReturnValues(th)
|
printReturnValues(t, th)
|
||||||
printBreakpointInfo(t, th, false)
|
printBreakpointInfo(t, th, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2598,47 +2606,47 @@ func printBreakpointInfo(t *Term, th *api.Thread, tracepointOnNewline bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
didprintnl = true
|
didprintnl = true
|
||||||
fmt.Println()
|
fmt.Fprintln(t.stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bpi.Goroutine != nil {
|
if bpi.Goroutine != nil {
|
||||||
tracepointnl()
|
tracepointnl()
|
||||||
writeGoroutineLong(t, os.Stdout, bpi.Goroutine, "\t")
|
writeGoroutineLong(t, t.stdout, bpi.Goroutine, "\t")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range bpi.Variables {
|
for _, v := range bpi.Variables {
|
||||||
tracepointnl()
|
tracepointnl()
|
||||||
fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
|
fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range bpi.Locals {
|
for _, v := range bpi.Locals {
|
||||||
tracepointnl()
|
tracepointnl()
|
||||||
if *bp.LoadLocals == longLoadConfig {
|
if *bp.LoadLocals == longLoadConfig {
|
||||||
fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
|
fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("\t%s: %s\n", v.Name, v.SinglelineString())
|
fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.SinglelineString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if bp.LoadArgs != nil && *bp.LoadArgs == longLoadConfig {
|
if bp.LoadArgs != nil && *bp.LoadArgs == longLoadConfig {
|
||||||
for _, v := range bpi.Arguments {
|
for _, v := range bpi.Arguments {
|
||||||
tracepointnl()
|
tracepointnl()
|
||||||
fmt.Printf("\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
|
fmt.Fprintf(t.stdout, "\t%s: %s\n", v.Name, v.MultilineString("\t", ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if bpi.Stacktrace != nil {
|
if bpi.Stacktrace != nil {
|
||||||
tracepointnl()
|
tracepointnl()
|
||||||
fmt.Printf("\tStack:\n")
|
fmt.Fprintf(t.stdout, "\tStack:\n")
|
||||||
printStack(t, os.Stdout, bpi.Stacktrace, "\t\t", false)
|
printStack(t, t.stdout, bpi.Stacktrace, "\t\t", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printTracepoint(t *Term, th *api.Thread, bpname string, fn *api.Function, args string, hasReturnValue bool) {
|
func printTracepoint(t *Term, th *api.Thread, bpname string, fn *api.Function, args string, hasReturnValue bool) {
|
||||||
if th.Breakpoint.Tracepoint {
|
if th.Breakpoint.Tracepoint {
|
||||||
fmt.Fprintf(os.Stderr, "> goroutine(%d): %s%s(%s)", th.GoroutineID, bpname, fn.Name(), args)
|
fmt.Fprintf(t.stdout, "> goroutine(%d): %s%s(%s)", th.GoroutineID, bpname, fn.Name(), args)
|
||||||
if !hasReturnValue {
|
if !hasReturnValue {
|
||||||
fmt.Println()
|
fmt.Fprintln(t.stdout)
|
||||||
}
|
}
|
||||||
printBreakpointInfo(t, th, !hasReturnValue)
|
printBreakpointInfo(t, th, !hasReturnValue)
|
||||||
}
|
}
|
||||||
@ -2647,12 +2655,12 @@ func printTracepoint(t *Term, th *api.Thread, bpname string, fn *api.Function, a
|
|||||||
for _, v := range th.ReturnValues {
|
for _, v := range th.ReturnValues {
|
||||||
retVals = append(retVals, v.SinglelineString())
|
retVals = append(retVals, v.SinglelineString())
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, " => (%s)\n", strings.Join(retVals, ","))
|
fmt.Fprintf(t.stdout, " => (%s)\n", strings.Join(retVals, ","))
|
||||||
}
|
}
|
||||||
if th.Breakpoint.TraceReturn || !hasReturnValue {
|
if th.Breakpoint.TraceReturn || !hasReturnValue {
|
||||||
if th.BreakpointInfo != nil && th.BreakpointInfo.Stacktrace != nil {
|
if th.BreakpointInfo != nil && th.BreakpointInfo.Stacktrace != nil {
|
||||||
fmt.Fprintf(os.Stderr, "\tStack:\n")
|
fmt.Fprintf(t.stdout, "\tStack:\n")
|
||||||
printStack(t, os.Stderr, th.BreakpointInfo.Stacktrace, "\t\t", false)
|
printStack(t, t.stdout, th.BreakpointInfo.Stacktrace, "\t\t", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2677,10 +2685,10 @@ func printfile(t *Term, filename string, line int, showArrow bool) error {
|
|||||||
fi, _ := file.Stat()
|
fi, _ := file.Stat()
|
||||||
lastModExe := t.client.LastModified()
|
lastModExe := t.client.LastModified()
|
||||||
if fi.ModTime().After(lastModExe) {
|
if fi.ModTime().After(lastModExe) {
|
||||||
fmt.Println("Warning: listing may not match stale executable")
|
fmt.Fprintln(t.stdout, "Warning: listing may not match stale executable")
|
||||||
}
|
}
|
||||||
|
|
||||||
return colorize.Print(t.stdout, file.Name(), file, line-lineCount, line+lineCount+1, arrowLine, t.colorEscapes)
|
return t.stdout.ColorizePrint(file.Name(), file, line-lineCount, line+lineCount+1, arrowLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExitRequestError is returned when the user
|
// ExitRequestError is returned when the user
|
||||||
@ -2779,7 +2787,7 @@ func (c *Commands) parseBreakpointAttrs(t *Term, ctx callContext, r io.Reader) e
|
|||||||
lineno++
|
lineno++
|
||||||
err := c.CallWithContext(scan.Text(), t, ctx)
|
err := c.CallWithContext(scan.Text(), t, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%d: %s\n", lineno, err.Error())
|
fmt.Fprintf(t.stdout, "%d: %s\n", lineno, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return scan.Err()
|
return scan.Err()
|
||||||
@ -2850,7 +2858,7 @@ func (c *Commands) executeFile(t *Term, name string) error {
|
|||||||
if _, isExitRequest := err.(ExitRequestError); isExitRequest {
|
if _, isExitRequest := err.(ExitRequestError); isExitRequest {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("%s:%d: %v\n", name, lineno, err)
|
fmt.Fprintf(t.stdout, "%s:%d: %v\n", name, lineno, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2889,7 +2897,7 @@ func checkpoint(t *Term, ctx callContext, args string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Checkpoint c%d created.\n", cpid)
|
fmt.Fprintf(t.stdout, "Checkpoint c%d created.\n", cpid)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2899,7 +2907,7 @@ func checkpoints(t *Term, ctx callContext, args string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w := new(tabwriter.Writer)
|
w := new(tabwriter.Writer)
|
||||||
w.Init(os.Stdout, 4, 4, 2, ' ', 0)
|
w.Init(t.stdout, 4, 4, 2, ' ', 0)
|
||||||
fmt.Fprintln(w, "ID\tWhen\tNote")
|
fmt.Fprintln(w, "ID\tWhen\tNote")
|
||||||
for _, cp := range cps {
|
for _, cp := range cps {
|
||||||
fmt.Fprintf(w, "c%d\t%s\t%s\n", cp.ID, cp.When, cp.Where)
|
fmt.Fprintf(w, "c%d\t%s\t%s\n", cp.ID, cp.When, cp.Where)
|
||||||
@ -2964,26 +2972,77 @@ func dump(t *Term, ctx callContext, args string) error {
|
|||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
if dumpState.ThreadsDone != dumpState.ThreadsTotal {
|
if dumpState.ThreadsDone != dumpState.ThreadsTotal {
|
||||||
fmt.Printf("\rDumping threads %d / %d...", dumpState.ThreadsDone, dumpState.ThreadsTotal)
|
fmt.Fprintf(t.stdout, "\rDumping threads %d / %d...", dumpState.ThreadsDone, dumpState.ThreadsTotal)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("\rDumping memory %d / %d...", dumpState.MemDone, dumpState.MemTotal)
|
fmt.Fprintf(t.stdout, "\rDumping memory %d / %d...", dumpState.MemDone, dumpState.MemTotal)
|
||||||
}
|
}
|
||||||
if !dumpState.Dumping {
|
if !dumpState.Dumping {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
dumpState = t.client.CoreDumpWait(1000)
|
dumpState = t.client.CoreDumpWait(1000)
|
||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Fprintf(t.stdout, "\n")
|
||||||
if dumpState.Err != "" {
|
if dumpState.Err != "" {
|
||||||
fmt.Printf("error dumping: %s\n", dumpState.Err)
|
fmt.Fprintf(t.stdout, "error dumping: %s\n", dumpState.Err)
|
||||||
} else if !dumpState.AllDone {
|
} else if !dumpState.AllDone {
|
||||||
fmt.Printf("canceled\n")
|
fmt.Fprintf(t.stdout, "canceled\n")
|
||||||
} else if dumpState.MemDone != dumpState.MemTotal {
|
} else if dumpState.MemDone != dumpState.MemTotal {
|
||||||
fmt.Printf("Core dump could be incomplete\n")
|
fmt.Fprintf(t.stdout, "Core dump could be incomplete\n")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func transcript(t *Term, ctx callContext, args string) error {
|
||||||
|
argv := strings.SplitN(args, " ", -1)
|
||||||
|
truncate := false
|
||||||
|
fileOnly := false
|
||||||
|
disable := false
|
||||||
|
path := ""
|
||||||
|
for _, arg := range argv {
|
||||||
|
switch arg {
|
||||||
|
case "-x":
|
||||||
|
fileOnly = true
|
||||||
|
case "-t":
|
||||||
|
truncate = true
|
||||||
|
case "-off":
|
||||||
|
disable = true
|
||||||
|
default:
|
||||||
|
if path != "" || strings.HasPrefix(arg, "-") {
|
||||||
|
return fmt.Errorf("unrecognized option %q", arg)
|
||||||
|
} else {
|
||||||
|
path = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if disable {
|
||||||
|
if path != "" {
|
||||||
|
return errors.New("-o option specified with an output path")
|
||||||
|
}
|
||||||
|
return t.stdout.CloseTranscript()
|
||||||
|
}
|
||||||
|
|
||||||
|
if path == "" {
|
||||||
|
return errors.New("no output path specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := os.O_APPEND | os.O_WRONLY | os.O_CREATE
|
||||||
|
if truncate {
|
||||||
|
flags |= os.O_TRUNC
|
||||||
|
}
|
||||||
|
fh, err := os.OpenFile(path, flags, 0660)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.stdout.CloseTranscript(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.stdout.TranscribeTo(fh, fileOnly)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
|
func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
|
||||||
thing := "breakpoint"
|
thing := "breakpoint"
|
||||||
if bp.Tracepoint {
|
if bp.Tracepoint {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -52,52 +53,28 @@ type FakeTerminal struct {
|
|||||||
const logCommandOutput = false
|
const logCommandOutput = false
|
||||||
|
|
||||||
func (ft *FakeTerminal) Exec(cmdstr string) (outstr string, err error) {
|
func (ft *FakeTerminal) Exec(cmdstr string) (outstr string, err error) {
|
||||||
outfh, err := ioutil.TempFile("", "cmdtestout")
|
var buf bytes.Buffer
|
||||||
if err != nil {
|
ft.Term.stdout.w = &buf
|
||||||
ft.t.Fatalf("could not create temporary file: %v", err)
|
ft.Term.starlarkEnv.Redirect(ft.Term.stdout)
|
||||||
}
|
err = ft.cmds.Call(cmdstr, ft.Term)
|
||||||
|
outstr = buf.String()
|
||||||
stdout, stderr, termstdout := os.Stdout, os.Stderr, ft.Term.stdout
|
|
||||||
os.Stdout, os.Stderr, ft.Term.stdout = outfh, outfh, outfh
|
|
||||||
defer func() {
|
|
||||||
os.Stdout, os.Stderr, ft.Term.stdout = stdout, stderr, termstdout
|
|
||||||
outfh.Close()
|
|
||||||
outbs, err1 := ioutil.ReadFile(outfh.Name())
|
|
||||||
if err1 != nil {
|
|
||||||
ft.t.Fatalf("could not read temporary output file: %v", err)
|
|
||||||
}
|
|
||||||
outstr = string(outbs)
|
|
||||||
if logCommandOutput {
|
if logCommandOutput {
|
||||||
ft.t.Logf("command %q -> %q", cmdstr, outstr)
|
ft.t.Logf("command %q -> %q", cmdstr, outstr)
|
||||||
}
|
}
|
||||||
os.Remove(outfh.Name())
|
ft.Term.stdout.Flush()
|
||||||
}()
|
|
||||||
err = ft.cmds.Call(cmdstr, ft.Term)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ft *FakeTerminal) ExecStarlark(starlarkProgram string) (outstr string, err error) {
|
func (ft *FakeTerminal) ExecStarlark(starlarkProgram string) (outstr string, err error) {
|
||||||
outfh, err := ioutil.TempFile("", "cmdtestout")
|
var buf bytes.Buffer
|
||||||
if err != nil {
|
ft.Term.stdout.w = &buf
|
||||||
ft.t.Fatalf("could not create temporary file: %v", err)
|
ft.Term.starlarkEnv.Redirect(ft.Term.stdout)
|
||||||
}
|
_, err = ft.Term.starlarkEnv.Execute("<stdin>", starlarkProgram, "main", nil)
|
||||||
|
outstr = buf.String()
|
||||||
stdout, stderr, termstdout := os.Stdout, os.Stderr, ft.Term.stdout
|
|
||||||
os.Stdout, os.Stderr, ft.Term.stdout = outfh, outfh, outfh
|
|
||||||
defer func() {
|
|
||||||
os.Stdout, os.Stderr, ft.Term.stdout = stdout, stderr, termstdout
|
|
||||||
outfh.Close()
|
|
||||||
outbs, err1 := ioutil.ReadFile(outfh.Name())
|
|
||||||
if err1 != nil {
|
|
||||||
ft.t.Fatalf("could not read temporary output file: %v", err)
|
|
||||||
}
|
|
||||||
outstr = string(outbs)
|
|
||||||
if logCommandOutput {
|
if logCommandOutput {
|
||||||
ft.t.Logf("command %q -> %q", starlarkProgram, outstr)
|
ft.t.Logf("command %q -> %q", starlarkProgram, outstr)
|
||||||
}
|
}
|
||||||
os.Remove(outfh.Name())
|
ft.Term.stdout.Flush()
|
||||||
}()
|
|
||||||
_, err = ft.Term.starlarkEnv.Execute("<stdin>", starlarkProgram, "main", nil)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1269,3 +1246,48 @@ func TestBreakpointEditing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTranscript(t *testing.T) {
|
||||||
|
withTestTerminal("math", t, func(term *FakeTerminal) {
|
||||||
|
term.MustExec("break main.main")
|
||||||
|
out := term.MustExec("continue")
|
||||||
|
if !strings.HasPrefix(out, "> main.main()") {
|
||||||
|
t.Fatalf("Wrong output for next: <%s>", out)
|
||||||
|
}
|
||||||
|
fh, err := ioutil.TempFile("", "test-transcript-*")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("TempFile: %v", err)
|
||||||
|
}
|
||||||
|
name := fh.Name()
|
||||||
|
fh.Close()
|
||||||
|
t.Logf("output to %q", name)
|
||||||
|
|
||||||
|
slurp := func() string {
|
||||||
|
b, err := ioutil.ReadFile(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not read transcript file: %v", err)
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
term.MustExec(fmt.Sprintf("transcript %s", name))
|
||||||
|
out = term.MustExec("list")
|
||||||
|
//term.MustExec("transcript -off")
|
||||||
|
if out != slurp() {
|
||||||
|
t.Logf("output of list %s", out)
|
||||||
|
t.Logf("contents of transcript: %s", slurp())
|
||||||
|
t.Errorf("transcript and command out differ")
|
||||||
|
}
|
||||||
|
|
||||||
|
term.MustExec(fmt.Sprintf("transcript -t -x %s", name))
|
||||||
|
out = term.MustExec(`print "hello"`)
|
||||||
|
if out != "" {
|
||||||
|
t.Errorf("output of print is %q but should have been suppressed by transcript", out)
|
||||||
|
}
|
||||||
|
if slurp() != "\"hello\"\n" {
|
||||||
|
t.Errorf("wrong contents of transcript: %q", slurp())
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Remove(name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -58,14 +58,14 @@ func (env *Env) REPL() error {
|
|||||||
if err := isCancelled(thread); err != nil {
|
if err := isCancelled(thread); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := rep(rl, thread, globals); err != nil {
|
if err := rep(rl, thread, globals, env.out); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Fprintln(env.out)
|
||||||
return env.exportGlobals(globals)
|
return env.exportGlobals(globals)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,12 +80,14 @@ const (
|
|||||||
//
|
//
|
||||||
// It returns an error (possibly readline.ErrInterrupt)
|
// It returns an error (possibly readline.ErrInterrupt)
|
||||||
// only if readline failed. Starlark errors are printed.
|
// only if readline failed. Starlark errors are printed.
|
||||||
func rep(rl *liner.State, thread *starlark.Thread, globals starlark.StringDict) error {
|
func rep(rl *liner.State, thread *starlark.Thread, globals starlark.StringDict, out EchoWriter) error {
|
||||||
|
defer out.Flush()
|
||||||
eof := false
|
eof := false
|
||||||
|
|
||||||
prompt := normalPrompt
|
prompt := normalPrompt
|
||||||
readline := func() ([]byte, error) {
|
readline := func() ([]byte, error) {
|
||||||
line, err := rl.Prompt(prompt)
|
line, err := rl.Prompt(prompt)
|
||||||
|
out.Echo(prompt + line)
|
||||||
if line == exitCommand {
|
if line == exitCommand {
|
||||||
eof = true
|
eof = true
|
||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
@ -122,7 +124,7 @@ func rep(rl *liner.State, thread *starlark.Thread, globals starlark.StringDict)
|
|||||||
|
|
||||||
// print
|
// print
|
||||||
if v != starlark.None {
|
if v != starlark.None {
|
||||||
fmt.Println(v)
|
fmt.Fprintln(out, v)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// compile
|
// compile
|
||||||
|
@ -3,6 +3,7 @@ package starbind
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -56,13 +57,15 @@ type Env struct {
|
|||||||
cancelfn context.CancelFunc
|
cancelfn context.CancelFunc
|
||||||
|
|
||||||
ctx Context
|
ctx Context
|
||||||
|
out EchoWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new starlark binding environment.
|
// New creates a new starlark binding environment.
|
||||||
func New(ctx Context) *Env {
|
func New(ctx Context, out EchoWriter) *Env {
|
||||||
env := &Env{}
|
env := &Env{}
|
||||||
|
|
||||||
env.ctx = ctx
|
env.ctx = ctx
|
||||||
|
env.out = out
|
||||||
|
|
||||||
env.env = env.starlarkPredeclare()
|
env.env = env.starlarkPredeclare()
|
||||||
env.env[dlvCommandBuiltinName] = starlark.NewBuiltin(dlvCommandBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
env.env[dlvCommandBuiltinName] = starlark.NewBuiltin(dlvCommandBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||||
@ -117,6 +120,18 @@ func New(ctx Context) *Env {
|
|||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Redirect redirects starlark output to out.
|
||||||
|
func (env *Env) Redirect(out EchoWriter) {
|
||||||
|
env.out = out
|
||||||
|
if env.thread != nil {
|
||||||
|
env.thread.Print = env.printFunc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *Env) printFunc() func(_ *starlark.Thread, msg string) {
|
||||||
|
return func(_ *starlark.Thread, msg string) { fmt.Fprintln(env.out, msg) }
|
||||||
|
}
|
||||||
|
|
||||||
// Execute executes a script. Path is the name of the file to execute and
|
// Execute executes a script. Path is the name of the file to execute and
|
||||||
// source is the source code to execute.
|
// source is the source code to execute.
|
||||||
// Source can be either a []byte, a string or a io.Reader. If source is nil
|
// Source can be either a []byte, a string or a io.Reader. If source is nil
|
||||||
@ -128,7 +143,7 @@ func (env *Env) Execute(path string, source interface{}, mainFnName string, args
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("panic executing starlark script: %v\n", err)
|
fmt.Fprintf(env.out, "panic executing starlark script: %v\n", err)
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
pc, file, line, ok := runtime.Caller(i)
|
pc, file, line, ok := runtime.Caller(i)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -139,7 +154,7 @@ func (env *Env) Execute(path string, source interface{}, mainFnName string, args
|
|||||||
if fn != nil {
|
if fn != nil {
|
||||||
fname = fn.Name()
|
fname = fn.Name()
|
||||||
}
|
}
|
||||||
fmt.Printf("%s\n\tin %s:%d\n", fname, file, line)
|
fmt.Fprintf(env.out, "%s\n\tin %s:%d\n", fname, file, line)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -193,7 +208,7 @@ func (env *Env) Cancel() {
|
|||||||
|
|
||||||
func (env *Env) newThread() *starlark.Thread {
|
func (env *Env) newThread() *starlark.Thread {
|
||||||
thread := &starlark.Thread{
|
thread := &starlark.Thread{
|
||||||
Print: func(_ *starlark.Thread, msg string) { fmt.Println(msg) },
|
Print: env.printFunc(),
|
||||||
}
|
}
|
||||||
env.contextMu.Lock()
|
env.contextMu.Lock()
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
@ -287,3 +302,9 @@ func decorateError(thread *starlark.Thread, err error) error {
|
|||||||
}
|
}
|
||||||
return fmt.Errorf("%s:%d: %v", pos.Filename(), pos.Line, err)
|
return fmt.Errorf("%s:%d: %v", pos.Filename(), pos.Line, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EchoWriter interface {
|
||||||
|
io.Writer
|
||||||
|
Echo(string)
|
||||||
|
Flush()
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package terminal
|
|||||||
//lint:file-ignore ST1005 errors here can be capitalized
|
//lint:file-ignore ST1005 errors here can be capitalized
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
@ -55,10 +56,9 @@ type Term struct {
|
|||||||
prompt string
|
prompt string
|
||||||
line *liner.State
|
line *liner.State
|
||||||
cmds *Commands
|
cmds *Commands
|
||||||
stdout io.Writer
|
stdout *transcriptWriter
|
||||||
InitFile string
|
InitFile string
|
||||||
displays []displayEntry
|
displays []displayEntry
|
||||||
colorEscapes map[colorize.Style]string
|
|
||||||
|
|
||||||
historyFile *os.File
|
historyFile *os.File
|
||||||
|
|
||||||
@ -99,34 +99,34 @@ func New(client service.Client, conf *config.Config) *Term {
|
|||||||
prompt: "(dlv) ",
|
prompt: "(dlv) ",
|
||||||
line: liner.NewLiner(),
|
line: liner.NewLiner(),
|
||||||
cmds: cmds,
|
cmds: cmds,
|
||||||
stdout: os.Stdout,
|
stdout: &transcriptWriter{w: os.Stdout},
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.ToLower(os.Getenv("TERM")) != "dumb" {
|
if strings.ToLower(os.Getenv("TERM")) != "dumb" {
|
||||||
t.stdout = getColorableWriter()
|
t.stdout.w = getColorableWriter()
|
||||||
t.colorEscapes = make(map[colorize.Style]string)
|
t.stdout.colorEscapes = make(map[colorize.Style]string)
|
||||||
t.colorEscapes[colorize.NormalStyle] = terminalResetEscapeCode
|
t.stdout.colorEscapes[colorize.NormalStyle] = terminalResetEscapeCode
|
||||||
wd := func(s string, defaultCode int) string {
|
wd := func(s string, defaultCode int) string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return fmt.Sprintf(terminalHighlightEscapeCode, defaultCode)
|
return fmt.Sprintf(terminalHighlightEscapeCode, defaultCode)
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
t.colorEscapes[colorize.KeywordStyle] = conf.SourceListKeywordColor
|
t.stdout.colorEscapes[colorize.KeywordStyle] = conf.SourceListKeywordColor
|
||||||
t.colorEscapes[colorize.StringStyle] = wd(conf.SourceListStringColor, ansiGreen)
|
t.stdout.colorEscapes[colorize.StringStyle] = wd(conf.SourceListStringColor, ansiGreen)
|
||||||
t.colorEscapes[colorize.NumberStyle] = conf.SourceListNumberColor
|
t.stdout.colorEscapes[colorize.NumberStyle] = conf.SourceListNumberColor
|
||||||
t.colorEscapes[colorize.CommentStyle] = wd(conf.SourceListCommentColor, ansiBrMagenta)
|
t.stdout.colorEscapes[colorize.CommentStyle] = wd(conf.SourceListCommentColor, ansiBrMagenta)
|
||||||
t.colorEscapes[colorize.ArrowStyle] = wd(conf.SourceListArrowColor, ansiYellow)
|
t.stdout.colorEscapes[colorize.ArrowStyle] = wd(conf.SourceListArrowColor, ansiYellow)
|
||||||
switch x := conf.SourceListLineColor.(type) {
|
switch x := conf.SourceListLineColor.(type) {
|
||||||
case string:
|
case string:
|
||||||
t.colorEscapes[colorize.LineNoStyle] = x
|
t.stdout.colorEscapes[colorize.LineNoStyle] = x
|
||||||
case int:
|
case int:
|
||||||
if (x > ansiWhite && x < ansiBrBlack) || x < ansiBlack || x > ansiBrWhite {
|
if (x > ansiWhite && x < ansiBrBlack) || x < ansiBlack || x > ansiBrWhite {
|
||||||
x = ansiBlue
|
x = ansiBlue
|
||||||
}
|
}
|
||||||
t.colorEscapes[colorize.LineNoStyle] = fmt.Sprintf(terminalHighlightEscapeCode, x)
|
t.stdout.colorEscapes[colorize.LineNoStyle] = fmt.Sprintf(terminalHighlightEscapeCode, x)
|
||||||
case nil:
|
case nil:
|
||||||
t.colorEscapes[colorize.LineNoStyle] = fmt.Sprintf(terminalHighlightEscapeCode, ansiBlue)
|
t.stdout.colorEscapes[colorize.LineNoStyle] = fmt.Sprintf(terminalHighlightEscapeCode, ansiBlue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,13 +135,16 @@ func New(client service.Client, conf *config.Config) *Term {
|
|||||||
client.SetReturnValuesLoadConfig(&lcfg)
|
client.SetReturnValuesLoadConfig(&lcfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.starlarkEnv = starbind.New(starlarkContext{t})
|
t.starlarkEnv = starbind.New(starlarkContext{t}, t.stdout)
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close returns the terminal to its previous mode.
|
// Close returns the terminal to its previous mode.
|
||||||
func (t *Term) Close() {
|
func (t *Term) Close() {
|
||||||
t.line.Close()
|
t.line.Close()
|
||||||
|
if err := t.stdout.CloseTranscript(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error closing transcript file: %v\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Term) sigintGuard(ch <-chan os.Signal, multiClient bool) {
|
func (t *Term) sigintGuard(ch <-chan os.Signal, multiClient bool) {
|
||||||
@ -150,7 +153,7 @@ func (t *Term) sigintGuard(ch <-chan os.Signal, multiClient bool) {
|
|||||||
t.starlarkEnv.Cancel()
|
t.starlarkEnv.Cancel()
|
||||||
state, err := t.client.GetStateNonBlocking()
|
state, err := t.client.GetStateNonBlocking()
|
||||||
if err == nil && state.Recording {
|
if err == nil && state.Recording {
|
||||||
fmt.Printf("received SIGINT, stopping recording (will not forward signal)\n")
|
fmt.Fprintf(t.stdout, "received SIGINT, stopping recording (will not forward signal)\n")
|
||||||
err := t.client.StopRecording()
|
err := t.client.StopRecording()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
@ -158,7 +161,7 @@ func (t *Term) sigintGuard(ch <-chan os.Signal, multiClient bool) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err == nil && state.CoreDumping {
|
if err == nil && state.CoreDumping {
|
||||||
fmt.Printf("received SIGINT, stopping dump\n")
|
fmt.Fprintf(t.stdout, "received SIGINT, stopping dump\n")
|
||||||
err := t.client.CoreDumpCancel()
|
err := t.client.CoreDumpCancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
@ -189,14 +192,14 @@ func (t *Term) sigintGuard(ch <-chan os.Signal, multiClient bool) {
|
|||||||
t.Close()
|
t.Close()
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
fmt.Println("only p or q allowed")
|
fmt.Fprintln(t.stdout, "only p or q allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("received SIGINT, stopping process (will not forward signal)\n")
|
fmt.Fprintf(t.stdout, "received SIGINT, stopping process (will not forward signal)\n")
|
||||||
_, err := t.client.Halt()
|
_, err := t.client.Halt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%v", err)
|
fmt.Fprintf(t.stdout, "%v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -278,11 +281,12 @@ func (t *Term) Run() (int, error) {
|
|||||||
cmdstr, err := t.promptForInput()
|
cmdstr, err := t.promptForInput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
fmt.Println("exit")
|
fmt.Fprintln(t.stdout, "exit")
|
||||||
return t.handleExit()
|
return t.handleExit()
|
||||||
}
|
}
|
||||||
return 1, fmt.Errorf("Prompt for input failed.\n")
|
return 1, fmt.Errorf("Prompt for input failed.\n")
|
||||||
}
|
}
|
||||||
|
t.stdout.Echo(t.prompt + cmdstr + "\n")
|
||||||
|
|
||||||
if strings.TrimSpace(cmdstr) == "" {
|
if strings.TrimSpace(cmdstr) == "" {
|
||||||
cmdstr = lastCmd
|
cmdstr = lastCmd
|
||||||
@ -309,6 +313,8 @@ func (t *Term) Run() (int, error) {
|
|||||||
fmt.Fprintf(os.Stderr, "Command failed: %s\n", err)
|
fmt.Fprintf(os.Stderr, "Command failed: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.stdout.Flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,10 +503,10 @@ func (t *Term) printDisplay(i int) {
|
|||||||
if isErrProcessExited(err) {
|
if isErrProcessExited(err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("%d: %s = error %v\n", i, expr, err)
|
fmt.Fprintf(t.stdout, "%d: %s = error %v\n", i, expr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("%d: %s = %s\n", i, val.Name, val.SinglelineStringFormatted(fmtstr))
|
fmt.Fprintf(t.stdout, "%d: %s = %s\n", i, val.Name, val.SinglelineStringFormatted(fmtstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Term) printDisplays() {
|
func (t *Term) printDisplays() {
|
||||||
@ -533,8 +539,90 @@ func (t *Term) longCommandCanceled() bool {
|
|||||||
return t.longCommandCancelFlag
|
return t.longCommandCancelFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RedirectTo redirects the output of this terminal to the specified writer.
|
||||||
|
func (t *Term) RedirectTo(w io.Writer) {
|
||||||
|
t.stdout.w = w
|
||||||
|
}
|
||||||
|
|
||||||
// isErrProcessExited returns true if `err` is an RPC error equivalent of proc.ErrProcessExited
|
// isErrProcessExited returns true if `err` is an RPC error equivalent of proc.ErrProcessExited
|
||||||
func isErrProcessExited(err error) bool {
|
func isErrProcessExited(err error) bool {
|
||||||
rpcError, ok := err.(rpc.ServerError)
|
rpcError, ok := err.(rpc.ServerError)
|
||||||
return ok && strings.Contains(rpcError.Error(), "has exited with status")
|
return ok && strings.Contains(rpcError.Error(), "has exited with status")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// transcriptWriter writes to a io.Writer and also, optionally, to a
|
||||||
|
// buffered file.
|
||||||
|
type transcriptWriter struct {
|
||||||
|
fileOnly bool
|
||||||
|
w io.Writer
|
||||||
|
file *bufio.Writer
|
||||||
|
fh io.Closer
|
||||||
|
colorEscapes map[colorize.Style]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *transcriptWriter) Write(p []byte) (nn int, err error) {
|
||||||
|
if !w.fileOnly {
|
||||||
|
nn, err = w.w.Write(p)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if w.file != nil {
|
||||||
|
return w.file.Write(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ColorizePrint prints to out a syntax highlighted version of the text read from
|
||||||
|
// reader, between lines startLine and endLine.
|
||||||
|
func (w *transcriptWriter) ColorizePrint(path string, reader io.ReadSeeker, startLine, endLine, arrowLine int) error {
|
||||||
|
var err error
|
||||||
|
if !w.fileOnly {
|
||||||
|
err = colorize.Print(w.w, path, reader, startLine, endLine, arrowLine, w.colorEscapes)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if w.file != nil {
|
||||||
|
reader.Seek(0, io.SeekStart)
|
||||||
|
return colorize.Print(w.file, path, reader, startLine, endLine, arrowLine, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Echo outputs str only to the optional transcript file.
|
||||||
|
func (w *transcriptWriter) Echo(str string) {
|
||||||
|
if w.file != nil {
|
||||||
|
w.file.WriteString(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush flushes the optional transcript file.
|
||||||
|
func (w *transcriptWriter) Flush() {
|
||||||
|
if w.file != nil {
|
||||||
|
w.file.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseTranscript closes the optional transcript file.
|
||||||
|
func (w *transcriptWriter) CloseTranscript() error {
|
||||||
|
if w.file == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w.file.Flush()
|
||||||
|
w.fileOnly = false
|
||||||
|
err := w.fh.Close()
|
||||||
|
w.file = nil
|
||||||
|
w.fh = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranscribeTo starts transcribing the output to the specified file. If
|
||||||
|
// fileOnly is true the output will only go to the file, output to the
|
||||||
|
// io.Writer will be suppressed.
|
||||||
|
func (w *transcriptWriter) TranscribeTo(fh io.WriteCloser, fileOnly bool) {
|
||||||
|
if w.file == nil {
|
||||||
|
w.CloseTranscript()
|
||||||
|
}
|
||||||
|
w.fh = fh
|
||||||
|
w.file = bufio.NewWriter(fh)
|
||||||
|
w.fileOnly = fileOnly
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user