pkg/dyninst/decode: Implement decoding of template messages - #42158
pkg/dyninst/decode: Implement decoding of template messages#42158grantseltzer wants to merge 0 commit into
Conversation
46873ad to
f992905
Compare
f992905 to
256a689
Compare
ajwerner
left a comment
There was a problem hiding this comment.
Flushing what I had typed, we discussed some of this offline.
| @@ -1,5 +1,7 @@ | |||
| // Code generated by "stringer -type=ProbeKind -linecomment -output probe_kind_string.go"; DO NOT EDIT. | |||
|
|
|||
| //go:build linux_bpf | |||
There was a problem hiding this comment.
did you just manually add this line? that's not really reasonable because it means that when other folks re-run go generate it'll go away. One thing you can do is write a shell script that go generate invokes that will both run stringer and then modify the files to add the relevant tags.
There was a problem hiding this comment.
Yes - I added it to one file and was just following the IDE telling me to do so, I missed that they were generated. I can write a script in another PR if needed but it doesn't seem to be causing any issues to not have this build tag here. I've just removed the change for now.
| // StringSegment is a string literal in the template | ||
| type StringSegment struct { | ||
| Value string | ||
| Index int |
There was a problem hiding this comment.
index into what? I remain skeptical of the value of this field (commented on that previously).
| @@ -144,15 +147,50 @@ type Variable struct { | |||
| // PCRange is the range of PC values that will be probed. | |||
| type PCRange = [2]uint64 | |||
|
|
|||
| // Template represents the concrete template structure for a probe | |||
There was a problem hiding this comment.
nit: periods on sentences to match the style in the rest of the file
| // RootTypeExpressionIndicies is a map of type IDs to the indices of the root expressions that correspond to this segment. | ||
| RootTypeExpressionIndicies map[TypeID]int |
There was a problem hiding this comment.
As I said last time around, I don't understand why this is a map.
There was a problem hiding this comment.
As discussed IRL, we now have an EventKind and EventExpressionIndex. This aligns the segment with an expression within an event.
| // ExpressionKind is the kind of expression that corresponds to this segment. | ||
| ExpressionKind EventKind |
There was a problem hiding this comment.
nit: this field should be called EventKind or trigger event or something -- it's not really the kind of the expression and it's not using the type RootExpressionKind.
This is really about which event this expression value should be taken from, right?
| return ir.Issue{ | ||
| Kind: ir.IssueKindUnsupportedFeature, | ||
| Message: "only 'ref' operation supported in templates", | ||
| } |
There was a problem hiding this comment.
We may want to know more about what's going on here. What if you switched on the type and for unsupported told us the operation?
| // Check if we already have an expression for this variable | ||
| if existingIdx, ok := variableExpressionSet[targetVar.Name]; ok { | ||
| // Reuse existing expression index | ||
| jsonSeg.RootTypeExpressionIndicies[id] = existingIdx |
There was a problem hiding this comment.
I don't understand why this is keyed on id
| expressions = append(expressions, expr) | ||
| } | ||
|
|
||
| // Process template segments if template exists | ||
| var variableExpressionSet map[string]int |
There was a problem hiding this comment.
It maps expression names to their index within the event. It enables us to not create duplicate expressions when processing template segments.
Static quality checks✅ Please find below the results from static quality gates Successful checksInfo
|
Regression DetectorRegression Detector ResultsMetrics dashboard Baseline: 6feee7a Optimization Goals: ✅ No significant changes detected
|
| perf | experiment | goal | Δ mean % | Δ mean % CI | trials | links |
|---|---|---|---|---|---|---|
| ✅ | docker_containers_cpu | % cpu utilization | -12.10 | [-13.66, -10.54] | 1 | Logs |
Fine details of change detection per experiment
| perf | experiment | goal | Δ mean % | Δ mean % CI | trials | links |
|---|---|---|---|---|---|---|
| ➖ | docker_containers_memory | memory utilization | +2.52 | [+2.17, +2.86] | 1 | Logs |
| ➖ | quality_gate_metrics_logs | memory utilization | +0.71 | [+0.52, +0.91] | 1 | Logs bounds checks dashboard |
| ➖ | quality_gate_idle_all_features | memory utilization | +0.47 | [+0.42, +0.51] | 1 | Logs bounds checks dashboard |
| ➖ | otlp_ingest_metrics | memory utilization | +0.31 | [+0.19, +0.43] | 1 | Logs |
| ➖ | ddot_logs | memory utilization | +0.31 | [+0.24, +0.37] | 1 | Logs |
| ➖ | otlp_ingest_logs | memory utilization | +0.20 | [+0.07, +0.33] | 1 | Logs |
| ➖ | file_to_blackhole_100ms_latency | egress throughput | +0.03 | [-0.57, +0.63] | 1 | Logs |
| ➖ | file_to_blackhole_1000ms_latency | egress throughput | +0.01 | [-0.60, +0.62] | 1 | Logs |
| ➖ | tcp_dd_logs_filter_exclude | ingress throughput | -0.00 | [-0.01, +0.01] | 1 | Logs |
| ➖ | file_tree | memory utilization | -0.00 | [-0.06, +0.06] | 1 | Logs |
| ➖ | file_to_blackhole_0ms_latency | egress throughput | -0.01 | [-0.61, +0.59] | 1 | Logs |
| ➖ | uds_dogstatsd_to_api | ingress throughput | -0.03 | [-0.27, +0.20] | 1 | Logs |
| ➖ | file_to_blackhole_500ms_latency | egress throughput | -0.09 | [-0.69, +0.51] | 1 | Logs |
| ➖ | quality_gate_idle | memory utilization | -0.13 | [-0.16, -0.09] | 1 | Logs bounds checks dashboard |
| ➖ | quality_gate_logs | % cpu utilization | -0.23 | [-3.03, +2.56] | 1 | Logs bounds checks dashboard |
| ➖ | ddot_metrics_sum_cumulative | memory utilization | -0.26 | [-0.38, -0.14] | 1 | Logs |
| ➖ | ddot_metrics_sum_cumulativetodelta_exporter | memory utilization | -0.35 | [-0.56, -0.14] | 1 | Logs |
| ➖ | ddot_metrics_sum_delta | memory utilization | -0.36 | [-0.52, -0.20] | 1 | Logs |
| ➖ | ddot_metrics | memory utilization | -0.49 | [-0.65, -0.32] | 1 | Logs |
| ➖ | uds_dogstatsd_20mb_12k_contexts_20_senders | memory utilization | -0.59 | [-0.66, -0.52] | 1 | Logs |
| ➖ | tcp_syslog_to_blackhole | ingress throughput | -0.81 | [-0.88, -0.73] | 1 | Logs |
| ✅ | docker_containers_cpu | % cpu utilization | -12.10 | [-13.66, -10.54] | 1 | Logs |
Bounds Checks: ❌ Failed
| perf | experiment | bounds_check_name | replicates_passed | links |
|---|---|---|---|---|
| ✅ | docker_containers_cpu | simple_check_run | 10/10 | |
| ❌ | docker_containers_memory | memory_usage | 9/10 | |
| ✅ | docker_containers_memory | simple_check_run | 10/10 | |
| ✅ | file_to_blackhole_0ms_latency | lost_bytes | 10/10 | |
| ✅ | file_to_blackhole_0ms_latency | memory_usage | 10/10 | |
| ✅ | file_to_blackhole_1000ms_latency | memory_usage | 10/10 | |
| ✅ | file_to_blackhole_100ms_latency | lost_bytes | 10/10 | |
| ✅ | file_to_blackhole_100ms_latency | memory_usage | 10/10 | |
| ✅ | file_to_blackhole_500ms_latency | lost_bytes | 10/10 | |
| ✅ | file_to_blackhole_500ms_latency | memory_usage | 10/10 | |
| ✅ | quality_gate_idle | intake_connections | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_idle | memory_usage | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_idle_all_features | intake_connections | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_idle_all_features | memory_usage | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_logs | intake_connections | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_logs | lost_bytes | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_logs | memory_usage | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_metrics_logs | cpu_usage | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_metrics_logs | intake_connections | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_metrics_logs | lost_bytes | 10/10 | bounds checks dashboard |
| ✅ | quality_gate_metrics_logs | memory_usage | 10/10 | bounds checks dashboard |
Explanation
Confidence level: 90.00%
Effect size tolerance: |Δ mean %| ≥ 5.00%
Performance changes are noted in the perf column of each table:
- ✅ = significantly better comparison variant performance
- ❌ = significantly worse comparison variant performance
- ➖ = no significant change in performance
A regression test is an A/B test of target performance in a repeatable rig, where "performance" is measured as "comparison variant minus baseline variant" for an optimization goal (e.g., ingress throughput). Due to intrinsic variability in measuring that goal, we can only estimate its mean value for each experiment; we report uncertainty in that value as a 90.00% confidence interval denoted "Δ mean % CI".
For each experiment, we decide whether a change in performance is a "regression" -- a change worth investigating further -- if all of the following criteria are true:
-
Its estimated |Δ mean %| ≥ 5.00%, indicating the change is big enough to merit a closer look.
-
Its 90.00% confidence interval "Δ mean % CI" does not contain zero, indicating that if our statistical model is accurate, there is at least a 90.00% chance there is a difference in performance between baseline and comparison variants.
-
Its configuration does not mark it "erratic".
CI Pass/Fail Decision
✅ Passed. All Quality Gates passed.
- quality_gate_logs, bounds check intake_connections: 10/10 replicas passed. Gate passed.
- quality_gate_logs, bounds check memory_usage: 10/10 replicas passed. Gate passed.
- quality_gate_logs, bounds check lost_bytes: 10/10 replicas passed. Gate passed.
- quality_gate_metrics_logs, bounds check memory_usage: 10/10 replicas passed. Gate passed.
- quality_gate_metrics_logs, bounds check intake_connections: 10/10 replicas passed. Gate passed.
- quality_gate_metrics_logs, bounds check cpu_usage: 10/10 replicas passed. Gate passed.
- quality_gate_metrics_logs, bounds check lost_bytes: 10/10 replicas passed. Gate passed.
- quality_gate_idle_all_features, bounds check memory_usage: 10/10 replicas passed. Gate passed.
- quality_gate_idle_all_features, bounds check intake_connections: 10/10 replicas passed. Gate passed.
- quality_gate_idle, bounds check memory_usage: 10/10 replicas passed. Gate passed.
- quality_gate_idle, bounds check intake_connections: 10/10 replicas passed. Gate passed.
cb47269 to
60598ae
Compare
| type SegmentList []TemplateSegment | ||
|
|
||
| // UnmarshalJSON implements custom JSON unmarshaling for SegmentList | ||
| func (sl *SegmentList) UnmarshalJSON(data []byte) error { |
There was a problem hiding this comment.
Transposing you comment here:
Rather than enforcing this structure at the point of unmarshaling, I think it'd be better to do it at the point of validation / in the call to
GetSegments(). Concretely: consider either keeping[]json.RawMessage. Alternatively, what if we just at this layer define a struct that has all three fields and then in validate we assert that only the correct subset are populated? I feel like that's pretty clean and quite efficient. Then when we iterate we can just cast to the proper implementation pointer for the corresponding subset of fields.
I see the advantage of your suggestion as having a concrete type here, as all the other fields of LogProbeCommon are. However I still like having an interface here. As Piotr described when he gave his opinion, it's more idiomatic to type switch rather than treat fields of a struct like a union. Are there any performance benefits to your suggestion?
| // RootTypeExpressionIndicies is a map of type IDs to the indices of the root expressions that correspond to this segment. | ||
| RootTypeExpressionIndicies map[TypeID]int |
There was a problem hiding this comment.
As discussed IRL, we now have an EventKind and EventExpressionIndex. This aligns the segment with an expression within an event.
| // StringSegment is a string literal in the template | ||
| type StringSegment struct { | ||
| Value string | ||
| Index int |
| @@ -1,5 +1,7 @@ | |||
| // Code generated by "stringer -type=ProbeKind -linecomment -output probe_kind_string.go"; DO NOT EDIT. | |||
|
|
|||
| //go:build linux_bpf | |||
There was a problem hiding this comment.
Yes - I added it to one file and was just following the IDE telling me to do so, I missed that they were generated. I can write a script in another PR if needed but it doesn't seem to be causing any issues to not have this build tag here. I've just removed the change for now.
| expressions = append(expressions, expr) | ||
| } | ||
|
|
||
| // Process template segments if template exists | ||
| var variableExpressionSet map[string]int |
There was a problem hiding this comment.
It maps expression names to their index within the event. It enables us to not create duplicate expressions when processing template segments.
104c079 to
e181496
Compare
e677d67 to
e4fb95b
Compare
e181496 to
da24bf5
Compare
e4fb95b to
0028212
Compare
da24bf5 to
c367e5a
Compare
d8ab7f5 to
8c35978
Compare
c367e5a to
3fd66e0
Compare
8c35978 to
707d545
Compare
3fd66e0 to
3f26031
Compare
a3b1aaa to
35209f7
Compare
ff99133 to
2ac4f4e
Compare
9142e6f to
4ee43e6
Compare
2ac4f4e to
b3a9d45
Compare
b9035d1 to
7c6b8d6
Compare
14a653a to
9b59a27
Compare
What does this PR do?
Adds support for decoding template messages captured via dynamic instrumentation.
Motivation
Allowing users to specify templates in Go DI/LD using the expression language. For now just variable referencing would be supported.
Describe how you validated your changes
Integration and unit tests.
Additional Notes