Skip to content

feat: Allow selecting instrumentation by PIDs#1321

Merged
damemi merged 3 commits into
open-telemetry:mainfrom
damemi:pid-selector
Feb 20, 2026
Merged

feat: Allow selecting instrumentation by PIDs#1321
damemi merged 3 commits into
open-telemetry:mainfrom
damemi:pid-selector

Conversation

@damemi
Copy link
Copy Markdown
Member

@damemi damemi commented Feb 17, 2026

This makes it possible to instrument by passing a PID, or list of PIDs, directly through the OBI config.

As a next step, it would be nice to be able to dynamically add/remove PIDs during runtime

Checklist

@damemi damemi requested a review from a team as a code owner February 17, 2026 16:21
@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 17, 2026

Codecov Report

❌ Patch coverage is 69.47368% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.61%. Comparing base (ce8adf4) to head (427f1e0).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
pkg/appolly/services/criteria.go 64.28% 11 Missing and 4 partials ⚠️
pkg/appolly/services/attr_regex.go 50.00% 6 Missing ⚠️
pkg/appolly/discover/matcher.go 81.48% 5 Missing ⚠️
pkg/appolly/services/attr_glob.go 83.33% 2 Missing ⚠️
examples/vendoring/vendoring.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #1321       +/-   ##
===========================================
+ Coverage   19.60%   43.61%   +24.01%     
===========================================
  Files         242      307       +65     
  Lines       28070    32962     +4892     
===========================================
+ Hits         5502    14376     +8874     
+ Misses      21922    17665     -4257     
- Partials      646      921      +275     
Flag Coverage Δ
integration-test 21.95% <29.62%> (+0.17%) ⬆️
integration-test-arm 0.00% <0.00%> (ø)
integration-test-vm-x86_64-5.15.152 ?
integration-test-vm-x86_64-6.10.6 ?
k8s-integration-test 2.35% <0.00%> (-0.01%) ⬇️
oats-test 0.00% <0.00%> (ø)
unittests 44.41% <75.60%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread pkg/appolly/services/attr_pid.go Outdated
Comment on lines +15 to +43
type PidSelector struct {
Pid app.PID
Name string
Namespace string
}

func (p *PidSelector) GetName() string { return p.Name }
func (p *PidSelector) GetNamespace() string { return p.Namespace }
func (p *PidSelector) GetPath() StringMatcher { return nilMatcher{} }
func (p *PidSelector) GetPathRegexp() StringMatcher { return nilMatcher{} }
func (p *PidSelector) GetOpenPorts() *PortEnum { return &PortEnum{} }
func (p *PidSelector) GetPID() (app.PID, bool) { return p.Pid, true }
func (p *PidSelector) IsContainersOnly() bool { return false }
func (p *PidSelector) GetExportModes() ExportModes { return ExportModes{} }
func (p *PidSelector) GetSamplerConfig() *SamplerConfig { return nil }
func (p *PidSelector) GetRoutesConfig() *CustomRoutesConfig { return nil }
func (p *PidSelector) MetricsConfig() perapp.SvcMetricsConfig { return perapp.SvcMetricsConfig{} }

func (p *PidSelector) RangeMetadata() iter.Seq2[string, StringMatcher] {
return func(_ func(string, StringMatcher) bool) {}
}

func (p *PidSelector) RangePodLabels() iter.Seq2[string, StringMatcher] {
return func(_ func(string, StringMatcher) bool) {}
}

func (p *PidSelector) RangePodAnnotations() iter.Seq2[string, StringMatcher] {
return func(_ func(string, StringMatcher) bool) {}
}
Copy link
Copy Markdown
Contributor

@mariomac mariomac Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think introducing a whole new Selector implementation type is not needed.

Analogously to what we do with the Open Ports attribute, you can just keep the GetPID() method in the Selector interface, plus implement it in the RegexSelector and GlobAttributes implementations, so any user can add the PID selection criteria to either the discovery > instrument or the (deprecated) discovery > services.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sgtm, updated to just keep GetPID in the Selector interface and added to RegexSelector and GlobAttributes

@damemi damemi force-pushed the pid-selector branch 2 times, most recently from 2967a76 to 20458cb Compare February 18, 2026 21:24
Copy link
Copy Markdown
Contributor

@grcevski grcevski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's looking good @damemi! I really like the level of testing done.

I had one question relating if we should make the PID rule absolute and exclusive and a suggestion about commoning the code with the ports selector. It it's effectively the same type of value, a list of ints and ranges.

Comment thread pkg/appolly/discover/matcher.go Outdated
return false
}
// PID matches; if this is PID-only criteria, skip path/port checks
if !a.GetPath().IsSet() && !a.GetPathRegexp().IsSet() && a.GetOpenPorts().Len() == 0 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering, why not just return if the PID matches and ignore the rest? We can put a validation rule on the selectors that will ensure that if you choose a PID, nothing else should be set.

I guess one reason to think this would need further selectors is if OBI is namespaced, but in that case it can only see a subset of the PIDs. I think we should make it absolute, you've set a PID on this selector you match that one directly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true, PID is a pretty explicit/atomic selector so it is weird to then scope up to other fields.

we might want to reevaluate selector criteria at some point, I could see reasons for using pid selector alongside others but for now I think just return pidInList() pretty much covers it here

Comment thread pkg/obi/config.go Outdated

// TargetPIDs is a list of process IDs to instrument. Supports YAML arrays (e.g. [1234, 5678]),
// a single YAML number, or env/comma-separated (e.g. OTEL_EBPF_TARGET_PID=1234,5678).
type TargetPIDs []uint32
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about reusing PortEnum and renaming it into something more appropriate? It supports something similar, e.g 80,443,8000-8999. I don't mind the new format, but it creates an inconsistency in how the end users would define this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me, renamed PortEnum to IntEnum (wdyt?)

Copy link
Copy Markdown
Contributor

@mariomac mariomac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@damemi damemi merged commit fc86a0b into open-telemetry:main Feb 20, 2026
52 checks passed
@grcevski
Copy link
Copy Markdown
Contributor

🥇 great first PR @damemi !

@MrAlias MrAlias added this to the v0.6.0 milestone Feb 23, 2026
@damemi damemi mentioned this pull request Mar 2, 2026
1 task
@MrAlias MrAlias mentioned this pull request Mar 5, 2026
damemi added a commit to odigos-io/odigos that referenced this pull request Apr 27, 2026
#### What this PR does / why we need it:
This adds `opentelemetry-ebpf-instrumentation` (OBI) as a supported
workload instrumentation distro.

The benefit of this is to provide additional coverage and offer users a
way to deploy OBI within our platform if they'd like to.

It relies on features we added upstream to OBI in
open-telemetry/opentelemetry-ebpf-instrumentation#1321
and
open-telemetry/opentelemetry-ebpf-instrumentation#1388
to support dynamic PID selection (as opposed to OBI's static service/exe
config approach).

The OBI `Instrumenter` is a single long-lived routine that handles
process events, filters/matches them based on config criteria (ie, which
PIDs we want), and attaches shared eBPF programs to those processes. The
`DynamicSelector` we added upstream provides a hook into the
`Instrumenter` to update the filter/matching criteria during runtime
without needing to restart OBI.

This adds OBI as an sdk/distro that can be used with any language. The
basic flow of the new distro is:
1. `NewOBIInstrumentationFactory()`: Initialize the OBI config and
DynamicSelector, but does not start the OBI `Instrumenter` routine yet.
This is to prevent the `Instrumenter` from running and using resources
if nothing is actually instrumented by OBI.
2. `factory.CreateInstrumentation(ctx, pid)`: If the OBI `Instrumenter`
is not started yet, this starts it and returns an `obiInstrumentation`
handle that stores that DynamicSelector and the instrumented PID. This
does not attach the PID to OBI yet.
3. `obiInstrumentation.Load()`: Uses the stored PID in the `o`
instrumentation object to call `DynamicSelector.AddPIDs(pid)` which
updates the running OBI manager to include/attach this process.
4. `obiInstrumentation.Close()`: Removes the stored PID in `o` with
`DynamicSelect.RemovePIDs(pid)` to update the OBI manager to
exclude/detach this process.

A caveat to this design is that it's difficult to _stop_ the
Instrumenter once it's started (ie, all OBI processes get uninstrumented
and we no longer need that routine) without complex async management. We
could do something like this on `Close()`:

```go
func Close() {
  f.Selector.RemovePIDs(pid)
  if len(f.Selector.GetPIDs()) == 0 {
    cancelObiCtx()
  }
}
```

But that would require a mutex, and it's possible that another OBI app
could come along in the meantime waiting to be instrumented while that
mutex is held, and the Instrumenter gets cancelled before the new app
gets added. Which would be very confusing to debug. I tried a lot of
different approaches and they were all messy. I think some more upstream
OBI changes around an actual handle on the instrumenter would help,
along with other use cases for holding a reference to the instrumenter
itself.

Overall:

* OBI Factory -- Called once at odiglet startup, runs nothing
* OBI CreateInstrumentation -- Starts OBI (if necessary) as a singleton
and creates the per-process `instrumentation` used by Odiglet
* Instrumentation Load -- Attaches the PID to the OBI singleton

Another possible upstream contribution would be a signal from OBI for
when it's actually started/running/ready to accept new PIDs which Load()
could wait on

This whole approach is slightly different from languages like Go, where
the *Odiglet* is the long lived process, so each
`CreateInstrumentation()` in Go calls the go-auto framework to load,
attach, and manage. In the case of OBI, the OBI Instrumenter handles
loading, attaching, and managing so Odiglet is a wrapped layer on top of
that for integration with our control plane.

This also adds OBI ebpf generation to the odiglet Dockerfile using the
upstream
[obi-generator[(https://github.com/open-telemetry/opentelemetry-ebpf-instrumentation/pkgs/container/obi-generator)

The OBI distro is added as its own module so it can be imported into
enterprise easier:
odigos-io/odigos-enterprise#2577 (see that PR
for details on OBI distro module)

UI Changes here: odigos-io/ui-kit#748

New C++ app in simple-demo for an e2e test here:
odigos-io/simple-demo#67

#### Changelog entry: Does this PR introduce a user-facing bug fix,
feature, dependency update, or breaking change??
<!--
This section will go in the release notes for this version. Is this
something users should be able to find easily?
If no, just write "NONE" in the release-note block below.
If yes, please add a release note in the block below describing this in
1-2 sentences for our changelog.
-->

```release-note
feat: Support opentelemetry-ebpf-instrumentation (OBI) for workload instrumentation
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants