1use crate::{
2 CacheState, CollectorCapability, DaemonState, ResourceSample, ResourceUsage, RunProfile,
3 RunState, StageMeasurement, StateMeasurement, StateTransition,
4};
5use serde::{Deserialize, Serialize};
6
7pub const PROFILE_SCHEMA_V2: &str = "keyhog-profile";
8pub const PROFILE_SCHEMA_V2_MAJOR: u16 = 2;
9pub const PROFILE_SCHEMA_V2_MINOR: u16 = 8;
10pub const PROFILE_ENVELOPE_V2_VERSION: u16 = 1;
11pub const CAUSAL_PROFILE_V2_VERSION: u16 = 8;
12pub const CAUSAL_IDENTITY_V2_VERSION: u16 = 1;
13pub const EVENT_SCHEMA_VERSION: u16 = 7;
14pub const METRIC_REGISTRY_VERSION: u16 = 6;
15pub const EXPORTER_VERSION: u16 = 1;
16pub const STAGE_CONCURRENCY_V2_VERSION: u16 = 1;
17pub const WORKER_OCCUPANCY_V2_VERSION: u16 = 1;
18pub const CACHE_EFFECTIVENESS_V2_VERSION: u16 = 1;
19pub const INDEXED_COUNTER_V2_VERSION: u16 = 1;
20pub const RETRY_RECORD_V2_VERSION: u16 = 1;
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
24#[serde(rename_all = "kebab-case")]
25pub enum EvidenceGap {
26 LegacyV1NotRecorded,
27 CollectorDisabled,
28 PermissionDenied,
29 Unsupported,
30 Unavailable,
31}
32
33#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
35#[serde(tag = "status", rename_all = "kebab-case")]
36pub enum Evidence<T> {
37 Recorded { value: T },
38 Unavailable { reason: EvidenceGap },
39}
40
41impl<T> Evidence<T> {
42 pub fn recorded(value: T) -> Self {
43 Self::Recorded { value }
44 }
45
46 pub const fn unavailable(reason: EvidenceGap) -> Self {
47 Self::Unavailable { reason }
48 }
49}
50
51fn legacy_gap<T>() -> Evidence<T> {
52 Evidence::unavailable(EvidenceGap::LegacyV1NotRecorded)
53}
54
55#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
57pub struct SchemaVersionV2 {
58 pub version: u16,
59 pub major: u16,
60 pub minor: u16,
61}
62
63#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
65pub struct ProducerIdentityV2 {
66 pub version: u16,
67 pub profile_crate_version: String,
68 pub exporter_version: u16,
69}
70
71#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
73pub struct ArtifactIntegrityV2 {
74 pub version: u16,
75 pub algorithm: String,
76 pub digest: String,
77}
78
79#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
81pub struct ProfileEnvelopeV2 {
82 pub version: u16,
83 pub schema: String,
84 pub schema_version: SchemaVersionV2,
85 pub event_schema_version: u16,
86 pub metric_registry_version: u16,
87 pub producer: ProducerIdentityV2,
88 pub integrity: Evidence<ArtifactIntegrityV2>,
89}
90
91#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
93pub struct HostIdentityV2 {
94 pub version: u16,
95 pub operating_system: Evidence<String>,
96 pub kernel_version: Evidence<String>,
97 pub architecture: Evidence<String>,
98 pub cpu_model: Evidence<String>,
99 pub logical_cpus: u32,
100 pub physical_cores: Evidence<u32>,
101 pub cpu_features_digest: Evidence<String>,
102 pub affinity_digest: Evidence<String>,
103 pub numa_digest: Evidence<String>,
104}
105
106#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
108pub struct BuildIdentityV2 {
109 pub version: u16,
110 pub binary_version: String,
111 pub binary_digest: Evidence<String>,
112 pub source_revision: Evidence<String>,
113 pub build_profile: Evidence<String>,
114 pub target_triple: Evidence<String>,
115 pub feature_digest: Evidence<String>,
116 pub compiler_identity: Evidence<String>,
117 pub allocator_identity: Evidence<String>,
118 pub linked_backend_digest: Evidence<String>,
119}
120
121#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
123pub struct DetectorIdentityV2 {
124 pub version: u16,
125 pub corpus_digest: String,
126 pub compiled_plan_digest: Evidence<String>,
127 pub enabled_detector_digest: Evidence<String>,
128 pub backend_database_digest: Evidence<String>,
129 pub external_provenance_digest: Evidence<String>,
130}
131
132#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
134pub struct ConfigIdentityV2 {
135 pub version: u16,
136 pub resolved_config_digest: String,
137 pub policy_digest: Evidence<String>,
138 pub preset: Evidence<String>,
139 pub protection_state: Evidence<String>,
140}
141
142#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
144pub struct SourceIdentityV2 {
145 pub version: u16,
146 pub adapters: Vec<String>,
147 pub target_digest: Evidence<String>,
148 pub partition_digest: Evidence<String>,
149}
150
151#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
153pub struct WorkloadIdentityV2 {
154 pub version: u16,
155 pub class: String,
156 pub raw_source_bytes: u64,
157 pub source_units: u64,
158 pub container_bytes: Evidence<u64>,
159 pub expanded_payload_bytes: Evidence<u64>,
160 pub derived_decoder_bytes: Evidence<u64>,
161 pub backend_dispatched_bytes: Evidence<u64>,
162 pub size_bucket: Evidence<String>,
163 pub fanout_bucket: Evidence<String>,
164}
165
166#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
168pub struct BatchRouteV2 {
169 pub version: u16,
170 pub batch_sequence: u64,
171 pub workload_key_digest: String,
172 pub requested_backend: String,
173 pub selected_backend: String,
174 pub completed_backend: String,
175 pub recovered_from_backend: Evidence<String>,
176}
177
178#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
180pub struct RouteIdentityV2 {
181 pub version: u16,
182 pub request_mode: String,
183 pub requested_backend: String,
184 pub selected_backend: Evidence<String>,
185 pub completed_backend: Evidence<String>,
186 pub autoroute_decision_digest: Evidence<String>,
187 pub batches: Vec<BatchRouteV2>,
188 #[serde(default)]
190 pub dropped_batches: u64,
191}
192
193impl RouteIdentityV2 {
194 pub fn from_recorded_batches(requested_backend: String, batches: Vec<BatchRouteV2>) -> Self {
196 Self::from_recorded_batches_with_drops(requested_backend, batches, 0)
197 }
198
199 pub fn from_recorded_batches_with_drops(
201 requested_backend: String,
202 batches: Vec<BatchRouteV2>,
203 dropped_batches: u64,
204 ) -> Self {
205 let request_mode = if requested_backend == "auto" {
206 "autoroute"
207 } else {
208 "explicit"
209 };
210 Self {
211 version: 1,
212 request_mode: request_mode.to_owned(),
213 selected_backend: aggregate_backend(&batches, |batch| &batch.selected_backend),
214 completed_backend: aggregate_backend(&batches, |batch| &batch.completed_backend),
215 requested_backend,
216 autoroute_decision_digest: Evidence::unavailable(EvidenceGap::Unavailable),
217 batches,
218 dropped_batches,
219 }
220 }
221}
222
223fn aggregate_backend(
224 batches: &[BatchRouteV2],
225 backend: impl Fn(&BatchRouteV2) -> &str,
226) -> Evidence<String> {
227 let mut labels = batches.iter().map(backend);
228 let Some(first) = labels.next() else {
229 return Evidence::unavailable(EvidenceGap::Unavailable);
230 };
231 if labels.all(|label| label == first) {
232 Evidence::recorded(first.to_owned())
233 } else {
234 Evidence::recorded("mixed".to_owned())
235 }
236}
237
238#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
240#[serde(rename_all = "kebab-case")]
241pub enum CacheLayerKindV2 {
242 LegacyAggregate,
243 Detector,
244 Merkle,
245 Autoroute,
246 Verifier,
247 Daemon,
248 PageCache,
249}
250
251#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
253pub struct CacheLayerV2 {
254 pub version: u16,
255 pub layer: CacheLayerKindV2,
256 pub state: CacheState,
257 pub generation: Evidence<String>,
258 pub digest: Evidence<String>,
259}
260
261#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
263pub struct DaemonIdentityV2 {
264 pub version: u16,
265 pub state: DaemonState,
266 pub generation: Evidence<String>,
267 pub request_id: Evidence<String>,
268 pub parent_request_id: Evidence<String>,
269 pub ready_age_ns: Evidence<u64>,
270}
271
272#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
274#[serde(rename_all = "kebab-case")]
275pub enum CoverageStateV2 {
276 Complete,
277 Partial,
278 Failed,
279 Cancelled,
280 Unknown,
281}
282
283#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
285pub struct OutcomeIdentityV2 {
286 pub version: u16,
287 pub status: RunState,
288 pub coverage: CoverageStateV2,
289 pub error_count: Evidence<u64>,
290 pub exit_code: Evidence<i32>,
291 pub findings_digest: Evidence<String>,
292 pub report_digest: Evidence<String>,
293}
294
295impl OutcomeIdentityV2 {
296 pub fn recorded(
298 status: RunState,
299 coverage: CoverageStateV2,
300 error_count: u64,
301 exit_code: i32,
302 findings_digest: Evidence<String>,
303 report_digest: Evidence<String>,
304 ) -> Self {
305 Self {
306 version: 1,
307 status,
308 coverage,
309 error_count: Evidence::recorded(error_count),
310 exit_code: Evidence::recorded(exit_code),
311 findings_digest,
312 report_digest,
313 }
314 }
315}
316
317#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
319pub struct CausalRunIdentityV2 {
320 pub version: u16,
321 pub run_id: String,
322 pub parent_run_id: Evidence<String>,
323 pub benchmark_pair_id: Evidence<String>,
324 pub repeat_group_id: Evidence<String>,
325 pub host: HostIdentityV2,
326 pub build: BuildIdentityV2,
327 pub detectors: DetectorIdentityV2,
328 pub config: ConfigIdentityV2,
329 pub source: SourceIdentityV2,
330 pub workload: WorkloadIdentityV2,
331 pub route: RouteIdentityV2,
332 pub caches: Vec<CacheLayerV2>,
333 pub daemon: DaemonIdentityV2,
334 pub outcome: OutcomeIdentityV2,
335 pub scanner_threads_requested: usize,
336 pub reader_threads_requested: Evidence<usize>,
337 pub reader_threads_resolved: Evidence<usize>,
338}
339
340#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
342#[serde(rename_all = "kebab-case")]
343#[repr(u8)]
344pub enum WorkOrigin {
345 #[default]
347 Root = 0,
348 Decoded = 1,
350 Derived = 2,
352 Retried = 3,
354}
355
356impl WorkOrigin {
357 pub const ALL: [Self; 4] = [Self::Root, Self::Decoded, Self::Derived, Self::Retried];
359
360 pub const fn is_attributed_work(self) -> bool {
362 !matches!(self, Self::Root)
363 }
364}
365
366#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
368pub struct SpanRecordV2 {
369 pub version: u16,
370 pub span_id: u64,
371 pub parent_span_id: Evidence<u64>,
372 pub metric_id: crate::MetricId,
373 pub start_ns: u64,
374 pub inclusive_ns: u64,
375 pub exclusive_ns: u64,
376 pub thread_id: u64,
377 pub task_id: Evidence<u64>,
378 #[serde(default = "legacy_gap")]
379 pub worker_id: Evidence<u64>,
380 #[serde(default)]
381 pub work_origin: WorkOrigin,
382 #[serde(default = "legacy_gap")]
384 pub hardware: Evidence<crate::hardware::SpanHardwareV2>,
385}
386
387#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
389pub struct DistributionBucketV2 {
390 pub version: u16,
391 pub lower_bound: u64,
392 pub upper_bound: u64,
393 pub count: u64,
394}
395
396#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
398pub struct MetricDistributionV2 {
399 pub version: u16,
400 pub metric_id: crate::MetricId,
401 pub call_count: u64,
402 pub minimum: u64,
403 pub maximum: u64,
404 pub buckets: Vec<DistributionBucketV2>,
405}
406
407#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
409pub struct QueueLinkV2 {
410 pub version: u16,
411 pub queue: crate::QueueId,
412 pub sequence: u64,
413 pub producer_thread_id: u64,
414 pub producer_elapsed_ns: u64,
415 pub consumer_thread_id: u64,
416 pub consumer_elapsed_ns: u64,
417}
418
419#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
421pub struct QueueDepthV2 {
422 pub version: u16,
423 pub queue: crate::QueueId,
424 pub current: u64,
425 pub high_water: u64,
426 pub enqueues: u64,
427 pub dequeues: u64,
428}
429
430#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
432pub struct WorkerLoadV2 {
433 pub version: u16,
434 pub worker_id: u64,
435 pub calls: u64,
436 pub elapsed_ns: u64,
437}
438
439#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
441pub struct WorkerImbalanceV2 {
442 pub version: u16,
443 pub worker_count: u64,
444 pub total_calls: u64,
445 pub total_elapsed_ns: u64,
446 pub max_share_ppm: u64,
448 pub median_share_ppm: u64,
450 pub idle_share_ppm: u64,
452 pub workers: Vec<WorkerLoadV2>,
453}
454
455#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
462pub struct StageConcurrencyV2 {
463 pub version: u16,
464 pub metric_id: crate::MetricId,
465 pub macro_stage_id: crate::MacroStageId,
466 pub calls: u64,
467 pub elapsed_ns: u64,
469 pub window_ns: u64,
471 pub first_start_ns: u64,
472 pub last_end_ns: u64,
473 pub worker_count: u64,
475 pub max_worker_elapsed_ns: u64,
477 pub concurrency_milli: u64,
479 pub declared_serial_ns: u64,
481 pub declared_serial_calls: u64,
482 pub bytes: u64,
484}
485
486#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
488pub struct WorkerOccupancyRowV2 {
489 pub version: u16,
490 pub worker_id: u64,
491 pub busy_ns: u64,
493 pub blocked_ns: u64,
495 pub calls: u64,
497}
498
499#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
501pub struct WorkerOccupancyV2 {
502 pub version: u16,
503 pub worker_count: u64,
505 pub active_worker_count: u64,
507 pub busy_ns: u64,
508 pub blocked_ns: u64,
509 pub calls: u64,
510 pub busiest_busy_ns: u64,
511 pub median_busy_ns: u64,
512 pub workers: Vec<WorkerOccupancyRowV2>,
513}
514
515#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
517pub struct CacheEffectivenessV2 {
518 pub version: u16,
519 pub cache: crate::CacheId,
520 pub hits: u64,
521 pub misses: u64,
522 pub hit_rate_ppm: u64,
524}
525
526#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
531pub struct RetryRecordV2 {
532 pub version: u16,
533 pub cause: crate::RetryCause,
534 pub attempts: u64,
535}
536
537#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
542pub struct IndexedCounterRecordV2 {
543 pub version: u16,
544 pub counter: crate::IndexedCounterId,
545 pub slots: Vec<u64>,
546 pub dropped_out_of_range: u64,
548}
549
550#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
552pub struct BlockedWaitRecordV2 {
553 pub version: u16,
554 pub metric_id: crate::MetricId,
555 pub macro_stage_id: crate::MacroStageId,
556 pub calls: u64,
557 pub blocked_ns: u64,
558}
559
560#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
562pub struct LatencyBucketV2 {
563 pub version: u16,
564 pub lower_bound_ns: u64,
565 pub upper_bound_ns: u64,
566 pub count: u64,
567}
568
569#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
571pub struct LatencyDistributionV2 {
572 pub version: u16,
573 pub metric_id: crate::MetricId,
574 pub macro_stage_id: crate::MacroStageId,
575 pub call_count: u64,
576 pub minimum_ns: u64,
577 pub maximum_ns: u64,
578 pub p50_ns: u64,
580 pub p90_ns: u64,
582 pub p95_ns: u64,
584 pub p99_ns: u64,
586 pub buckets: Vec<LatencyBucketV2>,
587}
588
589#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
591pub struct TypedMetricRecordV2 {
592 pub version: u16,
593 pub metric_id: crate::MetricId,
594 pub kind: crate::MetricKind,
595 pub value: u64,
596}
597
598#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
600pub struct PointEventV2 {
601 pub version: u16,
602 pub sequence: u64,
603 pub event_id: crate::EventId,
604 pub elapsed_ns: u64,
605 pub thread_id: u64,
606 pub value: u64,
607 #[serde(default = "legacy_gap")]
608 pub task_id: Evidence<u64>,
609 #[serde(default = "legacy_gap")]
610 pub worker_id: Evidence<u64>,
611}
612
613#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
615pub struct AnnotationV2 {
616 pub version: u16,
617 pub sequence: u64,
618 pub annotation_id: crate::AnnotationId,
619 pub elapsed_ns: u64,
620 pub thread_id: u64,
621 pub value: u64,
622 #[serde(default = "legacy_gap")]
623 pub task_id: Evidence<u64>,
624 #[serde(default = "legacy_gap")]
625 pub worker_id: Evidence<u64>,
626}
627
628#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
630pub struct EventStreamV2 {
631 pub version: u16,
632 pub availability: Evidence<bool>,
633 pub dropped_events: u64,
634 #[serde(default)]
635 pub dropped_span_events: u64,
636 #[serde(default)]
637 pub dropped_point_events: u64,
638 #[serde(default)]
639 pub dropped_annotations: u64,
640 #[serde(default)]
641 pub sampled_out_events: u64,
642 pub spans: Vec<SpanRecordV2>,
643 #[serde(default)]
644 pub point_events: Vec<PointEventV2>,
645 #[serde(default)]
646 pub annotations: Vec<AnnotationV2>,
647}
648
649#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
651pub struct CausalProfileV2 {
652 pub version: u16,
653 pub envelope: ProfileEnvelopeV2,
654 pub identity: CausalRunIdentityV2,
655 pub status: RunState,
656 pub wall_time_ns: u64,
657 pub stages: Vec<StageMeasurement>,
658 pub transitions: Vec<StateTransition>,
659 pub states: Vec<StateMeasurement>,
660 pub collectors: Vec<CollectorCapability>,
661 pub resource_samples: Vec<ResourceSample>,
662 pub resources: ResourceUsage,
663 #[serde(default)]
664 pub typed_metrics: Vec<TypedMetricRecordV2>,
665 #[serde(default)]
666 pub latency_distributions: Vec<LatencyDistributionV2>,
667 #[serde(default)]
669 pub stage_concurrency: Vec<StageConcurrencyV2>,
670 #[serde(default)]
672 pub worker_occupancy: Option<WorkerOccupancyV2>,
673 #[serde(default)]
675 pub queue_depths: Vec<QueueDepthV2>,
676 #[serde(default)]
678 pub blocked_waits: Vec<BlockedWaitRecordV2>,
679 #[serde(default)]
681 pub caches: Vec<CacheEffectivenessV2>,
682 #[serde(default)]
684 pub indexed_counters: Vec<IndexedCounterRecordV2>,
685 #[serde(default)]
687 pub retries: Vec<RetryRecordV2>,
688 #[serde(default)]
690 pub insight: Option<crate::insight::RunInsightV2>,
691 pub events: EventStreamV2,
692 #[serde(default = "legacy_gap")]
694 pub hardware: Evidence<crate::hardware::HardwareRunEvidenceV2>,
695 #[serde(default = "legacy_gap")]
697 pub system: Evidence<crate::system::SystemRunEvidenceV2>,
698}
699
700impl CausalProfileV2 {
701 pub fn from_v1(profile: RunProfile) -> Self {
703 let build = BuildIdentityV2::capture_legacy(&profile.identity.binary_version);
704 Self::from_v1_with_build(profile, build)
705 }
706
707 pub fn from_v1_with_build(profile: RunProfile, build: BuildIdentityV2) -> Self {
709 let RunProfile {
710 identity,
711 status,
712 wall_time_ns,
713 input_bytes,
714 input_units,
715 workload,
716 stages,
717 transitions,
718 states,
719 collectors,
720 resource_samples,
721 resources,
722 hardware,
723 system,
724 ..
725 } = profile;
726 let selected_backend = identity
727 .backend_selected
728 .clone()
729 .map_or_else(legacy_gap, Evidence::recorded);
730 let reader_threads_requested = identity
731 .reader_threads
732 .map_or_else(legacy_gap, Evidence::recorded);
733 let causal_identity = CausalRunIdentityV2 {
734 version: CAUSAL_IDENTITY_V2_VERSION,
735 run_id: identity.run_id,
736 parent_run_id: legacy_gap(),
737 benchmark_pair_id: legacy_gap(),
738 repeat_group_id: legacy_gap(),
739 host: HostIdentityV2::capture(),
740 build,
741 detectors: DetectorIdentityV2 {
742 version: 1,
743 corpus_digest: identity.detector_digest,
744 compiled_plan_digest: legacy_gap(),
745 enabled_detector_digest: legacy_gap(),
746 backend_database_digest: legacy_gap(),
747 external_provenance_digest: legacy_gap(),
748 },
749 config: ConfigIdentityV2 {
750 version: 1,
751 resolved_config_digest: identity.config_digest,
752 policy_digest: legacy_gap(),
753 preset: legacy_gap(),
754 protection_state: legacy_gap(),
755 },
756 source: SourceIdentityV2 {
757 version: 1,
758 adapters: vec![identity.source_kind],
759 target_digest: legacy_gap(),
760 partition_digest: legacy_gap(),
761 },
762 workload: WorkloadIdentityV2::capture(crate::WorkloadIdentityInput {
763 class: &identity.workload_class,
764 raw_source_bytes: input_bytes,
765 source_units: input_units,
766 container_bytes: workload.container_bytes,
767 expanded_payload_bytes: workload.expanded_payload_bytes,
768 derived_decoder_bytes: workload.derived_decoder_bytes,
769 backend_dispatched_bytes: workload.backend_dispatched_bytes,
770 }),
771 route: RouteIdentityV2 {
772 version: 1,
773 request_mode: "legacy-v1".to_owned(),
774 requested_backend: identity.backend_requested,
775 selected_backend,
776 completed_backend: legacy_gap(),
777 autoroute_decision_digest: legacy_gap(),
778 batches: Vec::new(),
779 dropped_batches: 0,
780 },
781 caches: vec![CacheLayerV2 {
782 version: 1,
783 layer: CacheLayerKindV2::LegacyAggregate,
784 state: identity.cache_state,
785 generation: legacy_gap(),
786 digest: legacy_gap(),
787 }],
788 daemon: DaemonIdentityV2 {
789 version: 1,
790 state: identity.daemon_state,
791 generation: legacy_gap(),
792 request_id: legacy_gap(),
793 parent_request_id: legacy_gap(),
794 ready_age_ns: legacy_gap(),
795 },
796 outcome: OutcomeIdentityV2 {
797 version: 1,
798 status,
799 coverage: CoverageStateV2::Unknown,
800 error_count: legacy_gap(),
801 exit_code: legacy_gap(),
802 findings_digest: legacy_gap(),
803 report_digest: legacy_gap(),
804 },
805 scanner_threads_requested: identity.scanner_threads,
806 reader_threads_requested,
807 reader_threads_resolved: legacy_gap(),
808 };
809 Self {
810 version: CAUSAL_PROFILE_V2_VERSION,
811 envelope: ProfileEnvelopeV2 {
812 version: PROFILE_ENVELOPE_V2_VERSION,
813 schema: PROFILE_SCHEMA_V2.to_owned(),
814 schema_version: SchemaVersionV2 {
815 version: 1,
816 major: PROFILE_SCHEMA_V2_MAJOR,
817 minor: PROFILE_SCHEMA_V2_MINOR,
818 },
819 event_schema_version: EVENT_SCHEMA_VERSION,
820 metric_registry_version: METRIC_REGISTRY_VERSION,
821 producer: ProducerIdentityV2 {
822 version: 1,
823 profile_crate_version: env!("CARGO_PKG_VERSION").to_owned(),
824 exporter_version: EXPORTER_VERSION,
825 },
826 integrity: legacy_gap(),
827 },
828 identity: causal_identity,
829 status,
830 wall_time_ns,
831 stages,
832 transitions,
833 states,
834 collectors,
835 resource_samples,
836 resources,
837 typed_metrics: Vec::new(),
838 latency_distributions: Vec::new(),
839 stage_concurrency: Vec::new(),
840 worker_occupancy: None,
841 queue_depths: Vec::new(),
842 blocked_waits: Vec::new(),
843 caches: Vec::new(),
844 indexed_counters: Vec::new(),
845 retries: Vec::new(),
846 insight: None,
847 events: EventStreamV2 {
848 version: 3,
849 availability: legacy_gap(),
850 dropped_events: 0,
851 dropped_span_events: 0,
852 dropped_point_events: 0,
853 dropped_annotations: 0,
854 sampled_out_events: 0,
855 spans: Vec::new(),
856 point_events: Vec::new(),
857 annotations: Vec::new(),
858 },
859 hardware,
860 system,
861 }
862 }
863}