Skip to content

Commit

Permalink
feat(pyroscope.java): Show debug info about profiled targets (#2304)
Browse files Browse the repository at this point in the history
* feat(pyroscope.java): Show debug info about profiled targets

This should make it clearer, what is currently profiled.

* Sort debuginfos by PID

Ensure order is consistent

* Collect per type byte information
  • Loading branch information
simonswine authored Jan 3, 2025
1 parent df1d04c commit f4fe420
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 1 deletion.
43 changes: 43 additions & 0 deletions internal/component/pyroscope/java/java.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import (
"context"
"fmt"
"os"
"sort"
"strconv"
"sync"
"time"

"github.com/grafana/alloy/internal/component"
"github.com/grafana/alloy/internal/component/discovery"
"github.com/grafana/alloy/internal/component/pyroscope"
"github.com/grafana/alloy/internal/component/pyroscope/java/asprof"
"github.com/grafana/alloy/internal/featuregate"
Expand Down Expand Up @@ -51,6 +54,31 @@ func init() {
})
}

type debugInfo struct {
ProfiledTargets []*debugInfoProfiledTarget `alloy:"profiled_targets,block"`
}

type debugInfoBytesPerType struct {
Type string `alloy:"type,attr"`
Bytes int64 `alloy:"bytes,attr"`
}

type debugInfoProfiledTarget struct {
TotalBytes int64 `alloy:"total_bytes,attr,optional"`
TotalSamples int64 `alloy:"total_samples,attr,optional"`
LastProfiled time.Time `alloy:"last_profiled,attr,optional"`
LastError time.Time `alloy:"last_error,attr,optional"`
LastProfileBytesPerType map[string]int64 `alloy:"last_profile_bytes_per_type,attr,optional"`
ErrorMsg string `alloy:"error_msg,attr,optional"`
PID int `alloy:"pid,attr"`
Target discovery.Target `alloy:"target,attr"`
}

var (
_ component.DebugComponent = (*javaComponent)(nil)
_ component.Component = (*javaComponent)(nil)
)

type javaComponent struct {
opts component.Options
args Arguments
Expand All @@ -69,6 +97,21 @@ func (j *javaComponent) Run(ctx context.Context) error {
return nil
}

func (j *javaComponent) DebugInfo() interface{} {
j.mutex.Lock()
defer j.mutex.Unlock()
var di debugInfo
di.ProfiledTargets = make([]*debugInfoProfiledTarget, 0, len(j.pid2process))
for _, proc := range j.pid2process {
di.ProfiledTargets = append(di.ProfiledTargets, proc.debugInfo())
}
// sort by pid
sort.Slice(di.ProfiledTargets, func(i, j int) bool {
return di.ProfiledTargets[i].PID < di.ProfiledTargets[j].PID
})
return &di
}

func (j *javaComponent) Update(args component.Arguments) error {
newArgs := args.(Arguments)
j.forwardTo.UpdateChildren(newArgs.ForwardTo)
Expand Down
56 changes: 55 additions & 1 deletion internal/component/pyroscope/java/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ type profilingLoop struct {
pid int
target discovery.Target
cancel context.CancelFunc
error error
dist *asprof.Distribution
jfrFile string
startTime time.Time
profiler *asprof.Profiler
sampleRate int

error error
lastError time.Time
lastPush time.Time
lastBytesPerType []debugInfoBytesPerType
totalBytes int64
totalSamples int64
}

func newProfilingLoop(pid int, target discovery.Target, logger log.Logger, profiler *asprof.Profiler, output *pyroscope.Fanout, cfg ProfilingConfig) *profilingLoop {
Expand Down Expand Up @@ -144,6 +150,11 @@ func (p *profilingLoop) push(jfrBytes []byte, startTime time.Time, endTime time.
return fmt.Errorf("failed to parse jfr: %w", err)
}
target := p.getTarget()
var totalSamples, totalBytes int64

// reset the per type bytes stats
p.lastBytesPerType = p.lastBytesPerType[:0]

for _, req := range profiles.Profiles {
metric := req.Metric
sz := req.Profile.SizeVT()
Expand All @@ -156,6 +167,13 @@ func (p *profilingLoop) push(jfrBytes []byte, startTime time.Time, endTime time.
ls.Set(labelServiceName, inferServiceName(target))
}

p.lastBytesPerType = append(p.lastBytesPerType, debugInfoBytesPerType{
Type: metric,
Bytes: int64(sz),
})
totalBytes += int64(sz)
totalSamples += int64(len(req.Profile.Sample))

profile, err := req.Profile.MarshalVT()
if err != nil {
_ = l.Log("msg", "failed to marshal profile", "err", err)
Expand All @@ -168,6 +186,12 @@ func (p *profilingLoop) push(jfrBytes []byte, startTime time.Time, endTime time.
continue
}
_ = l.Log("msg", "pushed jfr-pprof")

p.mutex.Lock()
p.lastPush = time.Now()
p.totalSamples += totalSamples
p.totalBytes += totalBytes
p.mutex.Unlock()
}
return nil
}
Expand Down Expand Up @@ -255,9 +279,39 @@ func (p *profilingLoop) onError(err error) bool {
p.mutex.Lock()
defer p.mutex.Unlock()
p.error = err
p.lastError = time.Now()
return alive
}

func (p *profilingLoop) debugInfo() *debugInfoProfiledTarget {
p.mutex.Lock()
defer p.mutex.Unlock()

d := &debugInfoProfiledTarget{
TotalBytes: p.totalBytes,
TotalSamples: p.totalSamples,
LastProfiled: p.lastPush,
LastError: p.lastError,
PID: p.pid,
Target: p.target,
}

// expose per profile type bytes
if len(p.lastBytesPerType) > 0 {
d.LastProfileBytesPerType = make(map[string]int64)
for _, b := range p.lastBytesPerType {
d.LastProfileBytesPerType[b.Type] += b.Bytes
}
}

// expose error message if given
if p.error != nil {
d.ErrorMsg = p.error.Error()
}
return d

}

func (p *profilingLoop) interval() time.Duration {
return p.getConfig().Interval
}
Expand Down

0 comments on commit f4fe420

Please sign in to comment.