1use std::collections::{BTreeMap, BTreeSet};
2use std::error::Error;
3use std::fmt;
4
5use super::{
6 Capability, EventKind, EventMask, InstrumentHandle, InstrumentMode, InstrumentRegistration,
7 RuntimeBackend, TargetDescriptor, TargetHandle,
8};
9
10#[path = "hub/delivery.rs"]
11mod delivery;
12pub use delivery::{
13 DeliveredEvent, DispatchReport, EventAccess, EventBatch, EventProjection, PortableProjection,
14 ProducerEvent,
15};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct InstrumentationAttachment {
19 pub instrument: InstrumentHandle,
20 pub target: TargetHandle,
21 pub granted_capabilities: BTreeSet<Capability>,
22 pub registration_order: u64,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct ControlLease {
27 instrument: InstrumentHandle,
28 target: TargetHandle,
29}
30
31impl ControlLease {
32 pub fn instrument(&self) -> &InstrumentHandle {
33 &self.instrument
34 }
35
36 pub fn target(&self) -> &TargetHandle {
37 &self.target
38 }
39}
40
41#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
42pub struct SessionCleanup {
43 pub instruments: usize,
44 pub targets: usize,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub enum InstrumentationError {
49 InvalidRegistration(String),
50 InvalidTarget(String),
51 Execution(String),
52 DuplicateInstrument(String),
53 DuplicateTarget(String),
54 DuplicateAttachment {
55 instrument_id: String,
56 target_id: String,
57 },
58 UnknownInstrument(String),
59 UnknownTarget(String),
60 StaleInstrumentHandle {
61 instrument_id: String,
62 generation: u64,
63 },
64 StaleTargetHandle {
65 target_id: String,
66 generation: u64,
67 },
68 SessionMismatch {
69 instrument_session: String,
70 target_session: String,
71 },
72 FilterMismatch {
73 instrument_id: String,
74 target_id: String,
75 },
76 UnsupportedCapabilities {
77 target_id: String,
78 backend: RuntimeBackend,
79 missing: BTreeSet<Capability>,
80 },
81 UnsupportedEvents {
82 target_id: String,
83 backend: RuntimeBackend,
84 events: BTreeSet<EventKind>,
85 },
86 ControlModeRequired(String),
87 AttachmentRequired {
88 instrument_id: String,
89 target_id: String,
90 },
91 ControlLeaseHeld {
92 target_id: String,
93 holder: String,
94 },
95 InvalidControlLease {
96 target_id: String,
97 instrument_id: String,
98 },
99}
100
101impl fmt::Display for InstrumentationError {
102 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 Self::InvalidRegistration(message) => {
105 write!(formatter, "instrumentation/invalid-registration: {message}")
106 }
107 Self::InvalidTarget(message) => {
108 write!(formatter, "instrumentation/invalid-target: {message}")
109 }
110 Self::Execution(message) => {
111 write!(formatter, "instrumentation/execution: {message}")
112 }
113 Self::DuplicateInstrument(id) => {
114 write!(formatter, "instrumentation/duplicate-instrument: {id}")
115 }
116 Self::DuplicateTarget(id) => {
117 write!(formatter, "instrumentation/duplicate-target: {id}")
118 }
119 Self::DuplicateAttachment {
120 instrument_id,
121 target_id,
122 } => write!(
123 formatter,
124 "instrumentation/duplicate-attachment: {instrument_id} -> {target_id}"
125 ),
126 Self::UnknownInstrument(id) => {
127 write!(formatter, "instrumentation/unknown-instrument: {id}")
128 }
129 Self::UnknownTarget(id) => {
130 write!(formatter, "instrumentation/unknown-target: {id}")
131 }
132 Self::StaleInstrumentHandle {
133 instrument_id,
134 generation,
135 } => write!(
136 formatter,
137 "instrumentation/stale-instrument: {instrument_id}@{generation}"
138 ),
139 Self::StaleTargetHandle {
140 target_id,
141 generation,
142 } => write!(
143 formatter,
144 "instrumentation/stale-target: {target_id}@{generation}"
145 ),
146 Self::SessionMismatch {
147 instrument_session,
148 target_session,
149 } => write!(
150 formatter,
151 "instrumentation/session-mismatch: instrument {instrument_session}, target {target_session}"
152 ),
153 Self::FilterMismatch {
154 instrument_id,
155 target_id,
156 } => write!(
157 formatter,
158 "instrumentation/filter-mismatch: {instrument_id} -> {target_id}"
159 ),
160 Self::UnsupportedCapabilities {
161 target_id,
162 backend,
163 missing,
164 } => write!(
165 formatter,
166 "instrumentation/unsupported-capabilities: target {target_id}, backend {}, missing {missing:?}",
167 backend.as_str()
168 ),
169 Self::UnsupportedEvents {
170 target_id,
171 backend,
172 events,
173 } => write!(
174 formatter,
175 "instrumentation/unsupported-events: target {target_id}, backend {}, events {events:?}",
176 backend.as_str()
177 ),
178 Self::ControlModeRequired(id) => {
179 write!(formatter, "instrumentation/control-mode-required: {id}")
180 }
181 Self::AttachmentRequired {
182 instrument_id,
183 target_id,
184 } => write!(
185 formatter,
186 "instrumentation/attachment-required: {instrument_id} -> {target_id}"
187 ),
188 Self::ControlLeaseHeld { target_id, holder } => write!(
189 formatter,
190 "instrumentation/control-lease-held: target {target_id}, holder {holder}"
191 ),
192 Self::InvalidControlLease {
193 target_id,
194 instrument_id,
195 } => write!(
196 formatter,
197 "instrumentation/invalid-control-lease: {instrument_id} -> {target_id}"
198 ),
199 }
200 }
201}
202
203impl Error for InstrumentationError {}
204
205#[derive(Debug, Clone)]
206struct InstrumentRecord {
207 handle: InstrumentHandle,
208 registration: InstrumentRegistration,
209 order: u64,
210}
211
212#[derive(Debug, Clone)]
213struct TargetRecord {
214 handle: TargetHandle,
215 descriptor: TargetDescriptor,
216}
217
218#[derive(Debug, Default)]
219pub struct InstrumentationHub {
220 registrations: BTreeMap<u64, InstrumentRecord>,
221 instrument_orders: BTreeMap<String, u64>,
222 instrument_generations: BTreeMap<String, u64>,
223 targets: BTreeMap<String, TargetRecord>,
224 target_generations: BTreeMap<String, u64>,
225 attachments: BTreeMap<(InstrumentHandle, TargetHandle), InstrumentationAttachment>,
226 enabled_events: EventMask,
227 control_leases: BTreeMap<TargetHandle, InstrumentHandle>,
228 next_registration_order: u64,
229 delivery: delivery::DeliveryState,
230}
231
232impl InstrumentationHub {
233 pub fn new() -> Self {
234 Self::default()
235 }
236
237 pub const fn enabled_events(&self) -> EventMask {
238 self.enabled_events
239 }
240
241 pub fn registration_count(&self) -> usize {
242 self.registrations.len()
243 }
244
245 pub fn target_count(&self) -> usize {
246 self.targets.len()
247 }
248
249 pub fn attachment_count(&self) -> usize {
250 self.attachments.len()
251 }
252
253 pub fn registrations(
254 &self,
255 ) -> impl Iterator<Item = (&InstrumentHandle, &InstrumentRegistration)> {
256 self.registrations
257 .values()
258 .map(|record| (&record.handle, &record.registration))
259 }
260
261 pub fn register(
262 &mut self,
263 registration: InstrumentRegistration,
264 ) -> Result<InstrumentHandle, InstrumentationError> {
265 registration
266 .validate()
267 .map_err(|message| InstrumentationError::InvalidRegistration(message.into()))?;
268 if self
269 .instrument_orders
270 .contains_key(®istration.instrument_id)
271 {
272 return Err(InstrumentationError::DuplicateInstrument(
273 registration.instrument_id,
274 ));
275 }
276 let generation = next_generation(
277 &mut self.instrument_generations,
278 ®istration.instrument_id,
279 );
280 let handle = InstrumentHandle::new(registration.instrument_id.clone(), generation);
281 let order = self.next_registration_order;
282 self.next_registration_order = self.next_registration_order.saturating_add(1);
283 self.instrument_orders
284 .insert(registration.instrument_id.clone(), order);
285 self.registrations.insert(
286 order,
287 InstrumentRecord {
288 handle: handle.clone(),
289 registration,
290 order,
291 },
292 );
293 Ok(handle)
294 }
295
296 pub fn register_target(
297 &mut self,
298 descriptor: TargetDescriptor,
299 ) -> Result<TargetHandle, InstrumentationError> {
300 descriptor
301 .validate()
302 .map_err(|message| InstrumentationError::InvalidTarget(message.into()))?;
303 if self.targets.contains_key(&descriptor.target_id) {
304 return Err(InstrumentationError::DuplicateTarget(descriptor.target_id));
305 }
306 let generation = next_generation(&mut self.target_generations, &descriptor.target_id);
307 let handle = TargetHandle::new(descriptor.target_id.clone(), generation);
308 self.targets.insert(
309 descriptor.target_id.clone(),
310 TargetRecord {
311 handle: handle.clone(),
312 descriptor,
313 },
314 );
315 Ok(handle)
316 }
317
318 pub fn attach(
319 &mut self,
320 instrument: &InstrumentHandle,
321 target: &TargetHandle,
322 ) -> Result<InstrumentationAttachment, InstrumentationError> {
323 let instrument_record = self.resolve_instrument(instrument)?;
324 let target_record = self.resolve_target(target)?;
325 if instrument_record.registration.session_id != target_record.descriptor.session_id {
326 return Err(InstrumentationError::SessionMismatch {
327 instrument_session: instrument_record.registration.session_id.clone(),
328 target_session: target_record.descriptor.session_id.clone(),
329 });
330 }
331 if !instrument_record
332 .registration
333 .filter
334 .matches(&target_record.descriptor)
335 {
336 return Err(InstrumentationError::FilterMismatch {
337 instrument_id: instrument.instrument_id().into(),
338 target_id: target.target_id().into(),
339 });
340 }
341 let unsupported_events = instrument_record
342 .registration
343 .events
344 .iter()
345 .copied()
346 .filter(|event| !event.supports_target(target_record.descriptor.kind))
347 .collect::<BTreeSet<_>>();
348 if !unsupported_events.is_empty() {
349 return Err(InstrumentationError::UnsupportedEvents {
350 target_id: target.target_id().into(),
351 backend: target_record.descriptor.backend.clone(),
352 events: unsupported_events,
353 });
354 }
355 let missing = instrument_record
356 .registration
357 .capabilities
358 .difference(&target_record.descriptor.capabilities)
359 .copied()
360 .collect::<BTreeSet<_>>();
361 if !missing.is_empty() {
362 return Err(InstrumentationError::UnsupportedCapabilities {
363 target_id: target.target_id().into(),
364 backend: target_record.descriptor.backend.clone(),
365 missing,
366 });
367 }
368 let key = (instrument.clone(), target.clone());
369 if self.attachments.contains_key(&key) {
370 return Err(InstrumentationError::DuplicateAttachment {
371 instrument_id: instrument.instrument_id().into(),
372 target_id: target.target_id().into(),
373 });
374 }
375 let attachment = InstrumentationAttachment {
376 instrument: instrument.clone(),
377 target: target.clone(),
378 granted_capabilities: instrument_record.registration.capabilities.clone(),
379 registration_order: instrument_record.order,
380 };
381 self.attachments.insert(key, attachment.clone());
382 self.recompute_event_mask();
383 Ok(attachment)
384 }
385
386 pub fn attachments_for_target(
387 &self,
388 target: &TargetHandle,
389 ) -> Result<Vec<&InstrumentationAttachment>, InstrumentationError> {
390 self.resolve_target(target)?;
391 let mut attachments = self
392 .attachments
393 .values()
394 .filter(|attachment| &attachment.target == target)
395 .collect::<Vec<_>>();
396 attachments.sort_by_key(|attachment| attachment.registration_order);
397 Ok(attachments)
398 }
399
400 pub fn enabled_for_target(
401 &self,
402 target: &TargetHandle,
403 event: EventKind,
404 ) -> Result<bool, InstrumentationError> {
405 self.resolve_target(target)?;
406 if !self.enabled_events.contains(event) {
407 return Ok(false);
408 }
409 Ok(self.attachments.iter().any(|((instrument, attached), _)| {
410 attached == target
411 && self
412 .resolve_instrument(instrument)
413 .map_or(false, |record| record.registration.events.contains(&event))
414 }))
415 }
416
417 pub fn acquire_control(
418 &mut self,
419 instrument: &InstrumentHandle,
420 target: &TargetHandle,
421 ) -> Result<ControlLease, InstrumentationError> {
422 let record = self.resolve_instrument(instrument)?;
423 self.resolve_target(target)?;
424 if record.registration.mode != InstrumentMode::Control {
425 return Err(InstrumentationError::ControlModeRequired(
426 instrument.instrument_id().into(),
427 ));
428 }
429 if !self
430 .attachments
431 .contains_key(&(instrument.clone(), target.clone()))
432 {
433 return Err(InstrumentationError::AttachmentRequired {
434 instrument_id: instrument.instrument_id().into(),
435 target_id: target.target_id().into(),
436 });
437 }
438 if let Some(holder) = self.control_leases.get(target) {
439 if holder != instrument {
440 return Err(InstrumentationError::ControlLeaseHeld {
441 target_id: target.target_id().into(),
442 holder: holder.instrument_id().into(),
443 });
444 }
445 }
446 self.control_leases
447 .insert(target.clone(), instrument.clone());
448 Ok(ControlLease {
449 instrument: instrument.clone(),
450 target: target.clone(),
451 })
452 }
453
454 pub fn release_control(&mut self, lease: &ControlLease) -> Result<(), InstrumentationError> {
455 self.resolve_instrument(&lease.instrument)?;
456 self.resolve_target(&lease.target)?;
457 match self.control_leases.get(&lease.target) {
458 Some(holder) if holder == &lease.instrument => {
459 self.control_leases.remove(&lease.target);
460 self.delivery.remove_directive(&lease.target);
461 Ok(())
462 }
463 _ => Err(InstrumentationError::InvalidControlLease {
464 target_id: lease.target.target_id().into(),
465 instrument_id: lease.instrument.instrument_id().into(),
466 }),
467 }
468 }
469
470 pub fn detach(&mut self, instrument: &InstrumentHandle) -> Result<(), InstrumentationError> {
471 let order = self.resolve_instrument(instrument)?.order;
472 self.registrations.remove(&order);
473 self.instrument_orders.remove(instrument.instrument_id());
474 self.attachments
475 .retain(|(candidate, _), _| candidate != instrument);
476 let controlled_targets = self
477 .control_leases
478 .iter()
479 .filter(|(_, holder)| *holder == instrument)
480 .map(|(target, _)| (*target).clone())
481 .collect::<Vec<_>>();
482 self.control_leases.retain(|_, holder| holder != instrument);
483 for target in &controlled_targets {
484 self.delivery.remove_directive(target);
485 }
486 self.delivery.remove_instrument(instrument);
487 self.recompute_event_mask();
488 Ok(())
489 }
490
491 pub fn remove_target(&mut self, target: &TargetHandle) -> Result<(), InstrumentationError> {
492 self.resolve_target(target)?;
493 self.targets.remove(target.target_id());
494 self.attachments
495 .retain(|(_, candidate), _| candidate != target);
496 self.control_leases.remove(target);
497 self.delivery.remove_target(target);
498 self.recompute_event_mask();
499 Ok(())
500 }
501
502 pub fn detach_session(&mut self, session_id: &str) -> SessionCleanup {
503 let instruments = self
504 .registrations
505 .values()
506 .filter(|record| record.registration.session_id == session_id)
507 .map(|record| record.handle.clone())
508 .collect::<Vec<_>>();
509 let targets = self
510 .targets
511 .values()
512 .filter(|record| record.descriptor.session_id == session_id)
513 .map(|record| record.handle.clone())
514 .collect::<Vec<_>>();
515 for instrument in &instruments {
516 self.detach(instrument)
517 .expect("session cleanup collected a live instrument handle");
518 }
519 for target in &targets {
520 self.remove_target(target)
521 .expect("session cleanup collected a live target handle");
522 }
523 SessionCleanup {
524 instruments: instruments.len(),
525 targets: targets.len(),
526 }
527 }
528
529 pub fn clear(&mut self) {
530 self.registrations.clear();
531 self.instrument_orders.clear();
532 self.targets.clear();
533 self.attachments.clear();
534 self.control_leases.clear();
535 self.enabled_events = EventMask::empty();
536 self.delivery.clear();
537 }
538
539 fn resolve_instrument(
540 &self,
541 handle: &InstrumentHandle,
542 ) -> Result<&InstrumentRecord, InstrumentationError> {
543 let Some(order) = self.instrument_orders.get(handle.instrument_id()) else {
544 return if self
545 .instrument_generations
546 .contains_key(handle.instrument_id())
547 {
548 Err(InstrumentationError::StaleInstrumentHandle {
549 instrument_id: handle.instrument_id().into(),
550 generation: handle.generation(),
551 })
552 } else {
553 Err(InstrumentationError::UnknownInstrument(
554 handle.instrument_id().into(),
555 ))
556 };
557 };
558 let record = self
559 .registrations
560 .get(order)
561 .expect("instrument index and registry must remain consistent");
562 if &record.handle == handle {
563 Ok(record)
564 } else {
565 Err(InstrumentationError::StaleInstrumentHandle {
566 instrument_id: handle.instrument_id().into(),
567 generation: handle.generation(),
568 })
569 }
570 }
571
572 fn resolve_target(&self, handle: &TargetHandle) -> Result<&TargetRecord, InstrumentationError> {
573 let Some(record) = self.targets.get(handle.target_id()) else {
574 return if self.target_generations.contains_key(handle.target_id()) {
575 Err(InstrumentationError::StaleTargetHandle {
576 target_id: handle.target_id().into(),
577 generation: handle.generation(),
578 })
579 } else {
580 Err(InstrumentationError::UnknownTarget(
581 handle.target_id().into(),
582 ))
583 };
584 };
585 if &record.handle == handle {
586 Ok(record)
587 } else {
588 Err(InstrumentationError::StaleTargetHandle {
589 target_id: handle.target_id().into(),
590 generation: handle.generation(),
591 })
592 }
593 }
594
595 fn recompute_event_mask(&mut self) {
596 let mut mask = EventMask::empty();
597 for (instrument, _) in self.attachments.keys() {
598 if let Ok(record) = self.resolve_instrument(instrument) {
599 for event in &record.registration.events {
600 mask.insert(*event);
601 }
602 }
603 }
604 self.enabled_events = mask;
605 }
606}
607
608fn next_generation(generations: &mut BTreeMap<String, u64>, id: &str) -> u64 {
609 let next = generations.entry(id.into()).or_insert(0);
610 let generation = *next;
611 *next = next.saturating_add(1);
612 generation
613}