Skip to content

Commit 193ad94

Browse files
committed
reporter: Upload symbols only if in given allowlist
If an allowlist is given Parca Agent will only upload symbols for binaries matching the strings that are specific. The match happens with `strings.Contains`. So `parca` matches both `parca` and `parca-agent`.
1 parent 33be919 commit 193ad94

File tree

4 files changed

+87
-37
lines changed

4 files changed

+87
-37
lines changed

config.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,11 @@ relabel_configs:
55
- source_labels: [__meta_process_executable_compiler]
66
target_label: compiler
77
action: replace
8+
9+
symbol_upload:
10+
allowlist: []
11+
# - parca
12+
# GPU workloads
13+
# - ollama
14+
# - cuda
15+
# - torch

config/config.go

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ var ErrEmptyConfig = errors.New("empty config")
2727
// Config holds all the configuration information for Parca Agent.
2828
type Config struct {
2929
RelabelConfigs []*relabel.Config `yaml:"relabel_configs,omitempty"`
30+
SymbolUpload SymbolUpload `yaml:"symbol_upload,omitempty"`
31+
}
32+
33+
type SymbolUpload struct {
34+
Allowlist []string `yaml:"allowlist,omitempty"`
3035
}
3136

3237
func (c Config) String() string {

main.go

+20-9
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ func mainWithExitCode() flags.ExitCode {
230230

231231
log.Info("report sent successfully")
232232

233-
if exiterr, ok := err.(*exec.ExitError); ok { //nolint: errorlint
233+
if exiterr, ok := err.(*exec.ExitError); ok { // nolint: errorlint
234234
return flags.ExitCode(exiterr.ExitCode())
235235
}
236236

@@ -283,7 +283,10 @@ func mainWithExitCode() flags.ExitCode {
283283
return flags.Failure("Failed to parse the included tracers: %s", err)
284284
}
285285

286-
var relabelConfigs []*relabel.Config
286+
var (
287+
relabelConfigs []*relabel.Config
288+
symbolsUploadAllowlist []string
289+
)
287290
if f.ConfigPath == "" {
288291
log.Info("no config file provided, using default config")
289292
} else {
@@ -297,11 +300,11 @@ func mainWithExitCode() flags.ExitCode {
297300
if cfgFile != nil {
298301
log.Infof("using config file: %s", f.ConfigPath)
299302
relabelConfigs = cfgFile.RelabelConfigs
303+
symbolsUploadAllowlist = cfgFile.SymbolUpload.Allowlist
300304
}
301305
}
302306

303-
traceHandlerCacheSize :=
304-
traceCacheSize(f.Profiling.Duration, f.Profiling.CPUSamplingFrequency, uint16(presentCores))
307+
traceHandlerCacheSize := traceCacheSize(f.Profiling.Duration, f.Profiling.CPUSamplingFrequency, uint16(presentCores))
305308

306309
intervals := times.New(5*time.Second, f.Profiling.Duration, f.Profiling.ProbabilisticInterval)
307310
times.StartRealtimeSync(mainCtx, f.ClockSyncInterval)
@@ -332,6 +335,7 @@ func mainWithExitCode() flags.ExitCode {
332335
f.Debuginfo.Strip,
333336
f.Debuginfo.UploadMaxParallel,
334337
f.Debuginfo.UploadDisable || isOfflineMode,
338+
symbolsUploadAllowlist,
335339
int64(f.Profiling.CPUSamplingFrequency),
336340
traceHandlerCacheSize,
337341
f.Debuginfo.UploadQueueSize,
@@ -478,8 +482,11 @@ func getTelemetryMetadata(numCPU int) map[string]string {
478482
// Simply increasing traceCacheIntervals is problematic when maxElementsPerInterval is large
479483
// (e.g. too many CPU cores present) as we end up using too much memory. A minimum size is
480484
// therefore used here.
481-
func traceCacheSize(monitorInterval time.Duration, samplesPerSecond int,
482-
presentCPUCores uint16) uint32 {
485+
func traceCacheSize(
486+
monitorInterval time.Duration,
487+
samplesPerSecond int,
488+
presentCPUCores uint16,
489+
) uint32 {
483490
const (
484491
traceCacheIntervals = 6
485492
traceCacheMinSize = 65536
@@ -494,8 +501,11 @@ func traceCacheSize(monitorInterval time.Duration, samplesPerSecond int,
494501
return util.NextPowerOfTwo(size)
495502
}
496503

497-
func maxElementsPerInterval(monitorInterval time.Duration, samplesPerSecond int,
498-
presentCPUCores uint16) uint32 {
504+
func maxElementsPerInterval(
505+
monitorInterval time.Duration,
506+
samplesPerSecond int,
507+
presentCPUCores uint16,
508+
) uint32 {
499509
return uint32(samplesPerSecond) * uint32(monitorInterval.Seconds()) * uint32(presentCPUCores)
500510
}
501511

@@ -504,7 +514,8 @@ func getTracePipe() (*os.File, error) {
504514
"/sys/kernel/debug/tracing",
505515
"/sys/kernel/tracing",
506516
"/tracing",
507-
"/trace"} {
517+
"/trace",
518+
} {
508519
t, err := os.Open(mnt + "/trace_pipe")
509520
if err == nil {
510521
return t, nil

reporter/parca_reporter.go

+54-28
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"errors"
1515
"fmt"
1616
"io"
17+
"log/slog"
1718
"os"
1819
"path"
1920
"strings"
@@ -29,8 +30,6 @@ import (
2930
"github.com/apache/arrow/go/v16/arrow/memory"
3031
lru "github.com/elastic/go-freelru"
3132
"github.com/klauspost/compress/zstd"
32-
"github.com/parca-dev/parca-agent/metrics"
33-
"github.com/parca-dev/parca-agent/reporter/metadata"
3433
"github.com/prometheus/client_golang/prometheus"
3534
"github.com/prometheus/common/model"
3635
"github.com/prometheus/prometheus/model/labels"
@@ -42,6 +41,9 @@ import (
4241
"go.opentelemetry.io/ebpf-profiler/libpf/xsync"
4342
otelmetrics "go.opentelemetry.io/ebpf-profiler/metrics"
4443
"go.opentelemetry.io/ebpf-profiler/reporter"
44+
45+
"github.com/parca-dev/parca-agent/metrics"
46+
"github.com/parca-dev/parca-agent/reporter/metadata"
4547
)
4648

4749
// Assert that we implement the full Reporter interface.
@@ -117,6 +119,9 @@ type ParcaReporter struct {
117119
// disableSymbolUpload disables the symbol upload.
118120
disableSymbolUpload bool
119121

122+
// symbolUploadAllowlist is checked before uploading symbols.
123+
symbolUploadAllowlist []string
124+
120125
// reportInterval is the interval at which to report data.
121126
reportInterval time.Duration
122127

@@ -167,8 +172,8 @@ func (r *ParcaReporter) SupportsReportTraceEvent() bool { return true }
167172

168173
// ReportTraceEvent enqueues reported trace events for the OTLP reporter.
169174
func (r *ParcaReporter) ReportTraceEvent(trace *libpf.Trace,
170-
meta *reporter.TraceEventMeta) {
171-
175+
meta *reporter.TraceEventMeta,
176+
) {
172177
// This is an LRU so we need to check every time if the stack is already
173178
// known, as it might have been evicted.
174179
if _, exists := r.stacks.Get(trace.Hash); !exists {
@@ -266,7 +271,6 @@ func (r *ParcaReporter) ExecutableKnown(fileID libpf.FileID) bool {
266271
// ExecutableMetadata accepts a fileID with the corresponding filename
267272
// and caches this information.
268273
func (r *ParcaReporter) ExecutableMetadata(args *reporter.ExecutableMetadataArgs) {
269-
270274
if args.Interp != libpf.Native {
271275
r.executables.Add(args.FileID, metadata.ExecInfo{
272276
FileName: args.FileName,
@@ -275,6 +279,20 @@ func (r *ParcaReporter) ExecutableMetadata(args *reporter.ExecutableMetadataArgs
275279
return
276280
}
277281

282+
if len(r.symbolUploadAllowlist) > 0 {
283+
var allowed bool
284+
for _, s := range r.symbolUploadAllowlist {
285+
if strings.Contains(args.FileName, s) {
286+
log.Infof("executable found in allowlist, file: '%s' matches '%s'", args.FileName, s)
287+
allowed = true
288+
break
289+
}
290+
}
291+
if !allowed {
292+
log.Debugf("ignoring executable, not found in allowlist, file: %s", args.FileName)
293+
}
294+
}
295+
278296
// Always attempt to upload, the uploader is responsible for deduplication.
279297
if !r.disableSymbolUpload {
280298
r.uploader.Upload(context.TODO(), args.FileID, args.GnuBuildID, args.Open)
@@ -363,8 +381,12 @@ func (r *ParcaReporter) ReportHostMetadata(metadataMap map[string]string) {
363381
}
364382

365383
// ReportHostMetadataBlocking enqueues host metadata.
366-
func (r *ParcaReporter) ReportHostMetadataBlocking(_ context.Context,
367-
metadataMap map[string]string, _ int, _ time.Duration) error {
384+
func (r *ParcaReporter) ReportHostMetadataBlocking(
385+
_ context.Context,
386+
_ map[string]string,
387+
_ int,
388+
_ time.Duration,
389+
) error {
368390
// noop
369391
return nil
370392
}
@@ -460,6 +482,7 @@ func New(
460482
stripTextSection bool,
461483
symbolUploadConcurrency int,
462484
disableSymbolUpload bool,
485+
symbolUploadAllowlist []string,
463486
samplesPerSecond int64,
464487
cacheSize uint32,
465488
uploaderQueueSize uint32,
@@ -524,21 +547,24 @@ func New(
524547
reg.MustRegister(sampleWriteRequestBytes)
525548
reg.MustRegister(stacktraceWriteRequestBytes)
526549

550+
slog.Info("reporter found allowlist", "list", symbolUploadAllowlist)
551+
527552
r := &ParcaReporter{
528-
stopSignal: make(chan libpf.Void),
529-
client: nil,
530-
executables: executables,
531-
labels: labels,
532-
frames: frames,
533-
sampleWriter: NewSampleWriter(mem),
534-
stacks: stacks,
535-
mem: mem,
536-
externalLabels: externalLabels,
537-
samplesPerSecond: samplesPerSecond,
538-
disableSymbolUpload: disableSymbolUpload,
539-
reportInterval: reportInterval,
540-
nodeName: nodeName,
541-
relabelConfigs: relabelConfigs,
553+
stopSignal: make(chan libpf.Void),
554+
client: nil,
555+
executables: executables,
556+
labels: labels,
557+
frames: frames,
558+
sampleWriter: NewSampleWriter(mem),
559+
stacks: stacks,
560+
mem: mem,
561+
externalLabels: externalLabels,
562+
samplesPerSecond: samplesPerSecond,
563+
disableSymbolUpload: disableSymbolUpload,
564+
symbolUploadAllowlist: symbolUploadAllowlist,
565+
reportInterval: reportInterval,
566+
nodeName: nodeName,
567+
relabelConfigs: relabelConfigs,
542568
metadataProviders: []metadata.MetadataProvider{
543569
metadata.NewProcessMetadataProvider(),
544570
metadata.NewMainExecutableMetadataProvider(executables),
@@ -575,8 +601,10 @@ func New(
575601
return r, nil
576602
}
577603

578-
const DATA_FILE_EXTENSION string = ".padata"
579-
const DATA_FILE_COMPRESSED_EXTENSION string = ".padata.zst"
604+
const (
605+
DATA_FILE_EXTENSION string = ".padata"
606+
DATA_FILE_COMPRESSED_EXTENSION string = ".padata.zst"
607+
)
580608

581609
// initialScan inspects the storage directory to determine its size, and whether there are any
582610
// uncompressed files lying around.
@@ -615,7 +643,7 @@ func initialScan(storagePath string) (map[string]uint64, []string, uint64, error
615643
}
616644

617645
func compressFile(file io.Reader, fpath, compressedFpath string) error {
618-
compressedLog, err := os.OpenFile(compressedFpath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0660)
646+
compressedLog, err := os.OpenFile(compressedFpath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o660)
619647
if err != nil {
620648
return fmt.Errorf("Failed to create compressed file %s for log rotation: %w", compressedFpath, err)
621649
}
@@ -641,7 +669,7 @@ func compressFile(file io.Reader, fpath, compressedFpath string) error {
641669

642670
func setupOfflineModeLog(fpath string) (*os.File, error) {
643671
// Open the log file
644-
file, err := os.OpenFile(fpath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0660)
672+
file, err := os.OpenFile(fpath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o660)
645673
if err != nil {
646674
return nil, fmt.Errorf("failed to create new offline mode file %s: %w", fpath, err)
647675
}
@@ -661,7 +689,6 @@ func (r *ParcaReporter) rotateOfflineModeLog() error {
661689
logFile, err := setupOfflineModeLog(fpath)
662690
if err != nil {
663691
return fmt.Errorf("Failed to create new log %s for offline mode: %w", fpath, err)
664-
665692
}
666693
// We are connected to the new log, let's take the old one and compress it
667694
r.offlineModeLogMu.Lock()
@@ -727,7 +754,7 @@ func (r *ParcaReporter) Start(mainCtx context.Context) error {
727754
}
728755

729756
if r.offlineModeConfig != nil {
730-
if err := os.MkdirAll(r.offlineModeConfig.StoragePath, 0770); err != nil {
757+
if err := os.MkdirAll(r.offlineModeConfig.StoragePath, 0o770); err != nil {
731758
return fmt.Errorf("error creating offline mode storage: %v", err)
732759
}
733760
go func() {
@@ -993,7 +1020,6 @@ func (r *ParcaReporter) reportDataToBackend(ctx context.Context, buf *bytes.Buff
9931020
}
9941021

9951022
rec, err = r.buildStacktraceRecord(ctx, stacktraceIDs)
996-
9971023
if err != nil {
9981024
return err
9991025
}

0 commit comments

Comments
 (0)