1package api
2
3import (
4	"fmt"
5	"io/ioutil"
6	"strconv"
7)
8
9// Debug can be used to query the /debug/pprof endpoints to gather
10// profiling information about the target agent.Debug
11//
12// The agent must have enable_debug set to true for profiling to be enabled
13// and for these endpoints to function.
14type Debug struct {
15	c *Client
16}
17
18// Debug returns a handle that exposes the internal debug endpoints.
19func (c *Client) Debug() *Debug {
20	return &Debug{c}
21}
22
23// Heap returns a pprof heap dump
24func (d *Debug) Heap() ([]byte, error) {
25	r := d.c.newRequest("GET", "/debug/pprof/heap")
26	_, resp, err := d.c.doRequest(r)
27	if err != nil {
28		return nil, fmt.Errorf("error making request: %s", err)
29	}
30	defer closeResponseBody(resp)
31
32	if resp.StatusCode != 200 {
33		return nil, generateUnexpectedResponseCodeError(resp)
34	}
35
36	// We return a raw response because we're just passing through a response
37	// from the pprof handlers
38	body, err := ioutil.ReadAll(resp.Body)
39	if err != nil {
40		return nil, fmt.Errorf("error decoding body: %s", err)
41	}
42
43	return body, nil
44}
45
46// Profile returns a pprof CPU profile for the specified number of seconds
47func (d *Debug) Profile(seconds int) ([]byte, error) {
48	r := d.c.newRequest("GET", "/debug/pprof/profile")
49
50	// Capture a profile for the specified number of seconds
51	r.params.Set("seconds", strconv.Itoa(seconds))
52
53	_, resp, err := d.c.doRequest(r)
54	if err != nil {
55		return nil, fmt.Errorf("error making request: %s", err)
56	}
57	defer closeResponseBody(resp)
58
59	if resp.StatusCode != 200 {
60		return nil, generateUnexpectedResponseCodeError(resp)
61	}
62
63	// We return a raw response because we're just passing through a response
64	// from the pprof handlers
65	body, err := ioutil.ReadAll(resp.Body)
66	if err != nil {
67		return nil, fmt.Errorf("error decoding body: %s", err)
68	}
69
70	return body, nil
71}
72
73// Trace returns an execution trace
74func (d *Debug) Trace(seconds int) ([]byte, error) {
75	r := d.c.newRequest("GET", "/debug/pprof/trace")
76
77	// Capture a trace for the specified number of seconds
78	r.params.Set("seconds", strconv.Itoa(seconds))
79
80	_, resp, err := d.c.doRequest(r)
81	if err != nil {
82		return nil, fmt.Errorf("error making request: %s", err)
83	}
84	defer closeResponseBody(resp)
85
86	if resp.StatusCode != 200 {
87		return nil, generateUnexpectedResponseCodeError(resp)
88	}
89
90	// We return a raw response because we're just passing through a response
91	// from the pprof handlers
92	body, err := ioutil.ReadAll(resp.Body)
93	if err != nil {
94		return nil, fmt.Errorf("error decoding body: %s", err)
95	}
96
97	return body, nil
98}
99
100// Goroutine returns a pprof goroutine profile
101func (d *Debug) Goroutine() ([]byte, error) {
102	r := d.c.newRequest("GET", "/debug/pprof/goroutine")
103
104	_, resp, err := d.c.doRequest(r)
105	if err != nil {
106		return nil, fmt.Errorf("error making request: %s", err)
107	}
108	defer closeResponseBody(resp)
109
110	if resp.StatusCode != 200 {
111		return nil, generateUnexpectedResponseCodeError(resp)
112	}
113
114	// We return a raw response because we're just passing through a response
115	// from the pprof handlers
116	body, err := ioutil.ReadAll(resp.Body)
117	if err != nil {
118		return nil, fmt.Errorf("error decoding body: %s", err)
119	}
120
121	return body, nil
122}
123