@@ -5,8 +5,12 @@ package service
55import (
66 "context"
77 "fmt"
8+ "runtime"
9+ "time"
810
11+ "github.com/Microsoft/hcsshim/internal/controller/vm"
912 "github.com/Microsoft/hcsshim/internal/shimdiag"
13+
1014 "github.com/containerd/errdefs"
1115)
1216
@@ -22,14 +26,83 @@ func (s *Service) diagExecInHostInternal(ctx context.Context, request *shimdiag.
2226 return & shimdiag.ExecProcessResponse {ExitCode : int32 (ec )}, nil
2327}
2428
25- func (s * Service ) diagTasksInternal (_ context.Context , _ * shimdiag.TasksRequest ) (* shimdiag.TasksResponse , error ) {
26- return nil , errdefs .ErrNotImplemented
29+ func (s * Service ) diagTasksInternal (ctx context.Context , request * shimdiag.TasksRequest ) (* shimdiag.TasksResponse , error ) {
30+ if s .vmController .State () != vm .StateRunning {
31+ return nil , fmt .Errorf ("cannot list tasks when vm is not running: %w" , errdefs .ErrFailedPrecondition )
32+ }
33+
34+ // Originally this method was intended to be used in a single pod setup and therefore,
35+ // we do not specify a TaskID in the request. Since our shim can support multiple pods,
36+ // we will modify this functionality so that we will return all tasks running in the UVM,
37+ // regardless of which pod they belong to.
38+
39+ resp := & shimdiag.TasksResponse {}
40+
41+ // todo: think about concurrency handling here.
42+ // Do we want to lock for entire duration or not.
43+ // This is a diagnostic method and therefore, should not have
44+ // performance implications in prod.
45+ s .mu .Lock ()
46+ defer s .mu .Unlock ()
47+
48+ for _ , podCtrl := range s .podControllers {
49+ containers , err := podCtrl .ListContainers ()
50+ if err != nil {
51+ return nil , fmt .Errorf ("failed to list containers: %w" , err )
52+ }
53+
54+ for containerID , ctrCtrl := range containers {
55+ t := & shimdiag.Task {ID : containerID }
56+
57+ if request .Execs {
58+ processes , err := ctrCtrl .ListProcesses ()
59+ if err != nil {
60+ return nil , fmt .Errorf ("failed to list processes for container %s: %w" , containerID , err )
61+ }
62+
63+ for _ , proc := range processes {
64+ status := proc .Status (false )
65+ t .Execs = append (t .Execs , & shimdiag.Exec {
66+ ID : status .ExecID ,
67+ State : status .Status .String (),
68+ })
69+ }
70+ }
71+
72+ resp .Tasks = append (resp .Tasks , t )
73+ }
74+ }
75+
76+ return resp , nil
2777}
2878
2979func (s * Service ) diagShareInternal (_ context.Context , _ * shimdiag.ShareRequest ) (* shimdiag.ShareResponse , error ) {
3080 return nil , errdefs .ErrNotImplemented
3181}
3282
33- func (s * Service ) diagStacksInternal (_ context.Context , _ * shimdiag.StacksRequest ) (* shimdiag.StacksResponse , error ) {
34- return nil , errdefs .ErrNotImplemented
83+ func (s * Service ) diagStacksInternal (ctx context.Context ) (* shimdiag.StacksResponse , error ) {
84+ if s .vmController .State () != vm .StateRunning {
85+ return nil , fmt .Errorf ("cannot dump stacks when vm is not running: %w" , errdefs .ErrFailedPrecondition )
86+ }
87+
88+ buf := make ([]byte , 4096 )
89+ for {
90+ buf = buf [:runtime .Stack (buf , true )]
91+ if len (buf ) < cap (buf ) {
92+ break
93+ }
94+ buf = make ([]byte , 2 * len (buf ))
95+ }
96+
97+ timedCtx , cancel := context .WithTimeout (ctx , 5 * time .Second )
98+ defer cancel ()
99+
100+ resp := & shimdiag.StacksResponse {Stacks : string (buf )}
101+ stacks , err := s .vmController .DumpStacks (timedCtx )
102+ if err != nil {
103+ return nil , fmt .Errorf ("failed to dump stacks: %w" , err )
104+ }
105+
106+ resp .GuestStacks = stacks
107+ return resp , nil
35108}
0 commit comments