diff --git a/experimental/example/app/app.py b/experimental/example/app/app.py index ed009947d..f1fc4f00a 100644 --- a/experimental/example/app/app.py +++ b/experimental/example/app/app.py @@ -46,13 +46,8 @@ def ignore_error(f): @app.route('/') def hello(): - counter_future = pool.submit(do_redis) - search_future = pool.submit(do_search) - qotd_future = pool.submit(do_qotd) - result = 'Hello World! I have been seen %s times.' % ignore_error(counter_future.result) - result += ignore_error(search_future.result) - result += ignore_error(qotd_future.result) - return result + qotd_msg = do_qotd() + return qotd_msg if __name__ == "__main__": logging.basicConfig(format='%(asctime)s %(levelname)s %(filename)s:%(lineno)d - %(message)s', level=logging.INFO) diff --git a/experimental/tracer/main/http.go b/experimental/tracer/main/http.go index bec8c23bb..e1fcdb42d 100644 --- a/experimental/tracer/main/http.go +++ b/experimental/tracer/main/http.go @@ -11,6 +11,7 @@ import ( dockerClient "github.com/fsouza/go-dockerclient" "github.com/weaveworks/scope/probe/docker" + "github.com/weaveworks/scope/probe/process" ) func respondWith(w http.ResponseWriter, code int, response interface{}) { @@ -23,10 +24,30 @@ func respondWith(w http.ResponseWriter, code int, response interface{}) { } } +func (t *tracer) pidsForContainer(id string) ([]int, error) { + var container docker.Container + t.docker.WalkContainers(func(c docker.Container) { + if c.ID() == id { + container = c + } + }) + + if container == nil { + return []int{}, fmt.Errorf("Not Found") + } + + pidTree, err := process.NewTree(process.NewWalker("/proc")) + if err != nil { + return []int{}, err + } + + return pidTree.GetChildren(container.PID()) +} + func (t *tracer) http(port int) { router := mux.NewRouter() - router.Methods("GET").Path("/containers").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + router.Methods("GET").Path("/container").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { containers := []*dockerClient.Container{} t.docker.WalkContainers(func(container docker.Container) { containers = append(containers, container.Container()) @@ -35,6 +56,34 @@ func (t *tracer) http(port int) { respondWith(w, http.StatusOK, containers) }) + router.Methods("POST").Path("/container/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + children, err := t.pidsForContainer(id) + if err != nil { + respondWith(w, http.StatusBadRequest, err.Error()) + return + } + + for _, pid := range children { + t.ptrace.TraceProcess(pid) + } + w.WriteHeader(204) + }) + + router.Methods("DELETE").Path("/container/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + children, err := t.pidsForContainer(id) + if err != nil { + respondWith(w, http.StatusBadRequest, err.Error()) + return + } + + for _, pid := range children { + t.ptrace.StopTracing(pid) + } + w.WriteHeader(204) + }) + router.Methods("GET").Path("/pid").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respondWith(w, http.StatusOK, t.ptrace.AttachedPIDs()) }) @@ -61,6 +110,8 @@ func (t *tracer) http(port int) { w.WriteHeader(204) }) + + router.Methods("GET").Path("/traces").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respondWith(w, http.StatusOK, t.store.Traces()) }) diff --git a/experimental/tracer/ptrace/thread.go b/experimental/tracer/ptrace/thread.go index 09c6eb715..61e3fdfbc 100644 --- a/experimental/tracer/ptrace/thread.go +++ b/experimental/tracer/ptrace/thread.go @@ -13,6 +13,7 @@ const ( READ = 0 WRITE = 1 CLOSE = 3 + STAT = 4 MMAP = 9 MPROTECT = 10 MADVISE = 28 @@ -96,7 +97,7 @@ func (t *thread) syscallStopped() { t.handleIO(&t.callRegs, &t.resultRegs) // we can ignore these syscalls - case SETROBUSTLIST, GETID, MMAP, MPROTECT, MADVISE, SOCKET, CLONE: + case SETROBUSTLIST, GETID, MMAP, MPROTECT, MADVISE, SOCKET, CLONE, STAT: return default: diff --git a/experimental/tracer/ui/index.html b/experimental/tracer/ui/index.html index 938f9a728..a2463a987 100644 --- a/experimental/tracer/ui/index.html +++ b/experimental/tracer/ui/index.html @@ -60,7 +60,7 @@ } function updateContainers() { - $.get("/containers").done(function (data) { + $.get("/container").done(function (data) { data.sort(function (a, b) { if (a.Name > b.Name) { return 1; @@ -99,14 +99,14 @@ $("body").on("click", "div.mainview button.start", function() { var id = $(this).parent().data("containerId") var container = containersByID[id] - $.post(sprintf("/pid/%d", container.State.Pid)) + $.post(sprintf("/container/%s", container.Id)) }) $("body").on("click", "div.mainview button.stop", function() { var id = $(this).parent().data("containerId") var container = containersByID[id] $.ajax({ - url: sprintf("/pid/%d", container.State.Pid), + url: sprintf("/container/%s", container.Id), type: 'DELETE', }); }) diff --git a/probe/process/tree.go b/probe/process/tree.go index 2d03a9c0f..67f750b56 100644 --- a/probe/process/tree.go +++ b/probe/process/tree.go @@ -7,6 +7,7 @@ import ( // Tree represents all processes on the machine. type Tree interface { GetParent(pid int) (int, error) + GetChildren(pid int) ([]int, error) } type tree struct { @@ -32,3 +33,30 @@ func (pt *tree) GetParent(pid int) (int, error) { return proc.PPID, nil } + +// GetChildren +func (pt *tree) GetChildren(pid int) ([]int, error) { + _, ok := pt.processes[pid] + if !ok { + return []int{}, fmt.Errorf("PID %d not found", pid) + } + + var isChild func(id int) bool + isChild = func(id int) bool { + p, ok := pt.processes[id] + if !ok || p.PPID == 0 { + return false + } else if p.PPID == pid { + return true + } + return isChild(p.PPID) + } + + children := []int{pid} + for id, _ := range pt.processes { + if isChild(id) { + children = append(children, id) + } + } + return children, nil +}