1use std::cell::RefCell;
2use std::collections::BTreeSet;
3use std::error::Error;
4use std::fmt;
5use std::rc::{Rc, Weak};
6
7use super::{
8 Capability, ControlLease, EventBatch, EventDelivery, InstrumentDirective, InstrumentHandle,
9 InstrumentMode, InstrumentRegistration, InstrumentationAttachment, InstrumentationError,
10 InstrumentationHub, RuntimeBackend, TargetDescriptor, TargetHandle,
11};
12
13#[derive(Clone)]
18pub struct NativeInstrumentation {
19 session_id: String,
20 hub: Weak<RefCell<InstrumentationHub>>,
21}
22
23#[derive(Clone)]
25pub struct NativeInstrumentHandle {
26 session_id: String,
27 hub: Weak<RefCell<InstrumentationHub>>,
28 handle: InstrumentHandle,
29}
30
31#[derive(Clone)]
33pub struct NativeTargetHandle {
34 session_id: String,
35 hub: Weak<RefCell<InstrumentationHub>>,
36 handle: TargetHandle,
37}
38
39#[derive(Clone)]
41pub struct NativeControlLease {
42 session_id: String,
43 hub: Weak<RefCell<InstrumentationHub>>,
44 lease: ControlLease,
45}
46
47#[derive(Clone)]
50pub struct NativeAttachment {
51 instrument: NativeInstrumentHandle,
52 target: NativeTargetHandle,
53 granted_capabilities: BTreeSet<Capability>,
54 registration_order: u64,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub enum NativeInstrumentationError {
59 RuntimeClosed {
60 session_id: String,
61 },
62 UnknownSession(String),
63 SessionClosed(String),
64 CrossRuntimeHandle {
65 kind: &'static str,
66 },
67 CrossSessionHandle {
68 kind: &'static str,
69 expected: String,
70 actual: String,
71 },
72 UnsupportedMode(InstrumentMode),
73 UnsupportedDelivery(&'static str),
74 UnsupportedCapabilities {
75 target_id: String,
76 backend: RuntimeBackend,
77 requested: BTreeSet<Capability>,
78 potential: BTreeSet<Capability>,
79 missing: BTreeSet<Capability>,
80 },
81 Hub(InstrumentationError),
82}
83
84impl fmt::Display for NativeInstrumentationError {
85 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 Self::RuntimeClosed { session_id } => write!(
88 formatter,
89 "instrumentation/native-runtime-closed: session {session_id}"
90 ),
91 Self::UnknownSession(session_id) => {
92 write!(formatter, "instrumentation/native-unknown-session: {session_id}")
93 }
94 Self::SessionClosed(session_id) => {
95 write!(formatter, "instrumentation/native-session-closed: {session_id}")
96 }
97 Self::CrossRuntimeHandle { kind } => {
98 write!(formatter, "instrumentation/native-cross-runtime-handle: {kind}")
99 }
100 Self::CrossSessionHandle {
101 kind,
102 expected,
103 actual,
104 } => write!(
105 formatter,
106 "instrumentation/native-cross-session-handle: {kind}, expected {expected}, actual {actual}"
107 ),
108 Self::UnsupportedMode(mode) => write!(
109 formatter,
110 "instrumentation/native-unsupported-mode: {mode:?}"
111 ),
112 Self::UnsupportedDelivery(delivery) => write!(
113 formatter,
114 "instrumentation/native-unsupported-delivery: {delivery}"
115 ),
116 Self::UnsupportedCapabilities {
117 target_id,
118 backend,
119 requested,
120 potential,
121 missing,
122 } => write!(
123 formatter,
124 "instrumentation/native-unsupported-capabilities: target {target_id}, backend {}, requested {requested:?}, potential {potential:?}, missing {missing:?}",
125 backend.as_str()
126 ),
127 Self::Hub(error) => fmt::Display::fmt(error, formatter),
128 }
129 }
130}
131
132impl Error for NativeInstrumentationError {}
133
134impl From<InstrumentationError> for NativeInstrumentationError {
135 fn from(error: InstrumentationError) -> Self {
136 Self::Hub(error)
137 }
138}
139
140impl fmt::Debug for NativeInstrumentation {
141 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
142 formatter
143 .debug_struct("NativeInstrumentation")
144 .field("session_id", &self.session_id)
145 .field("active", &self.is_active())
146 .finish_non_exhaustive()
147 }
148}
149
150impl fmt::Debug for NativeInstrumentHandle {
151 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
152 formatter
153 .debug_struct("NativeInstrumentHandle")
154 .field("session_id", &self.session_id)
155 .field("instrument_id", &self.handle.instrument_id())
156 .field("generation", &self.handle.generation())
157 .finish_non_exhaustive()
158 }
159}
160
161impl fmt::Debug for NativeTargetHandle {
162 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
163 formatter
164 .debug_struct("NativeTargetHandle")
165 .field("session_id", &self.session_id)
166 .field("target_id", &self.handle.target_id())
167 .field("generation", &self.handle.generation())
168 .finish_non_exhaustive()
169 }
170}
171
172impl fmt::Debug for NativeControlLease {
173 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
174 formatter
175 .debug_struct("NativeControlLease")
176 .field("session_id", &self.session_id)
177 .field("instrument_id", &self.lease.instrument().instrument_id())
178 .field("target_id", &self.lease.target().target_id())
179 .finish_non_exhaustive()
180 }
181}
182
183impl fmt::Debug for NativeAttachment {
184 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
185 formatter
186 .debug_struct("NativeAttachment")
187 .field("instrument", &self.instrument)
188 .field("target", &self.target)
189 .field("granted_capabilities", &self.granted_capabilities)
190 .field("registration_order", &self.registration_order)
191 .finish()
192 }
193}
194
195impl NativeInstrumentation {
196 pub(crate) fn new(session_id: impl Into<String>, hub: Rc<RefCell<InstrumentationHub>>) -> Self {
197 Self {
198 session_id: session_id.into(),
199 hub: Rc::downgrade(&hub),
200 }
201 }
202
203 pub fn session_id(&self) -> &str {
204 &self.session_id
205 }
206
207 pub fn is_active(&self) -> bool {
208 self.hub.upgrade().is_some()
209 }
210
211 pub fn register(
215 &self,
216 registration: InstrumentRegistration,
217 ) -> Result<NativeInstrumentHandle, NativeInstrumentationError> {
218 if registration.session_id != self.session_id {
219 return Err(self.cross_session("registration", ®istration.session_id));
220 }
221 if registration.mode == InstrumentMode::Transform {
222 return Err(NativeInstrumentationError::UnsupportedMode(
223 InstrumentMode::Transform,
224 ));
225 }
226 if registration.delivery == EventDelivery::Callback {
227 return Err(NativeInstrumentationError::UnsupportedDelivery(
228 "callback binding is not available in this tranche",
229 ));
230 }
231 let hub = self.hub()?;
232 let handle = hub.borrow_mut().register(registration)?;
233 Ok(self.instrument_handle(handle))
234 }
235
236 pub fn bind_target(
239 &self,
240 target: &TargetHandle,
241 ) -> Result<NativeTargetHandle, NativeInstrumentationError> {
242 let hub = self.hub()?;
243 let descriptor = {
244 let hub = hub.borrow();
245 hub.target_descriptor(target)?.clone()
246 };
247 if descriptor.session_id != self.session_id {
248 return Err(self.cross_session("target", &descriptor.session_id));
249 }
250 Ok(self.target_handle(target.clone()))
251 }
252
253 pub fn registration(
254 &self,
255 instrument: &NativeInstrumentHandle,
256 ) -> Result<InstrumentRegistration, NativeInstrumentationError> {
257 self.ensure_instrument(instrument)?;
258 let hub = self.hub()?;
259 let registration = {
260 let hub = hub.borrow();
261 hub.instrument_registration(&instrument.handle)?.clone()
262 };
263 Ok(registration)
264 }
265
266 pub fn target_descriptor(
267 &self,
268 target: &NativeTargetHandle,
269 ) -> Result<TargetDescriptor, NativeInstrumentationError> {
270 self.ensure_target(target)?;
271 let hub = self.hub()?;
272 let descriptor = {
273 let hub = hub.borrow();
274 hub.target_descriptor(&target.handle)?.clone()
275 };
276 Ok(descriptor)
277 }
278
279 pub fn attach(
280 &self,
281 instrument: &NativeInstrumentHandle,
282 target: &NativeTargetHandle,
283 ) -> Result<NativeAttachment, NativeInstrumentationError> {
284 self.ensure_instrument(instrument)?;
285 self.ensure_target(target)?;
286 let hub = self.hub()?;
287 let (requested, potential) = {
288 let hub = hub.borrow();
289 (
290 hub.instrument_registration(&instrument.handle)?
291 .capabilities
292 .clone(),
293 hub.target_descriptor(&target.handle)?.capabilities.clone(),
294 )
295 };
296 let attachment = match hub.borrow_mut().attach(&instrument.handle, &target.handle) {
297 Ok(attachment) => attachment,
298 Err(InstrumentationError::UnsupportedCapabilities {
299 target_id,
300 backend,
301 missing,
302 }) => {
303 return Err(NativeInstrumentationError::UnsupportedCapabilities {
304 target_id,
305 backend,
306 requested,
307 potential,
308 missing,
309 });
310 }
311 Err(error) => return Err(error.into()),
312 };
313 Ok(self.attachment(attachment))
314 }
315
316 pub fn granted_capabilities(
317 &self,
318 instrument: &NativeInstrumentHandle,
319 target: &NativeTargetHandle,
320 ) -> Result<BTreeSet<Capability>, NativeInstrumentationError> {
321 self.ensure_instrument(instrument)?;
322 self.ensure_target(target)?;
323 let hub = self.hub()?;
324 let capabilities = {
325 let hub = hub.borrow();
326 hub.attachments_for_target(&target.handle)?
327 .into_iter()
328 .find(|attachment| attachment.instrument == instrument.handle)
329 .map(|attachment| attachment.granted_capabilities.clone())
330 .ok_or_else(|| InstrumentationError::AttachmentRequired {
331 instrument_id: instrument.instrument_id().into(),
332 target_id: target.target_id().into(),
333 })?
334 };
335 Ok(capabilities)
336 }
337
338 pub fn queued_event_count(
339 &self,
340 instrument: &NativeInstrumentHandle,
341 ) -> Result<usize, NativeInstrumentationError> {
342 self.ensure_instrument(instrument)?;
343 let hub = self.hub()?;
344 let count = hub.borrow().queued_event_count(&instrument.handle)?;
345 Ok(count)
346 }
347
348 pub fn drain_events(
349 &self,
350 instrument: &NativeInstrumentHandle,
351 ) -> Result<EventBatch, NativeInstrumentationError> {
352 self.ensure_instrument(instrument)?;
353 let hub = self.hub()?;
354 let batch = hub.borrow_mut().drain_events(&instrument.handle)?;
355 Ok(batch)
356 }
357
358 pub fn detach(
359 &self,
360 instrument: &NativeInstrumentHandle,
361 ) -> Result<(), NativeInstrumentationError> {
362 self.ensure_instrument(instrument)?;
363 let hub = self.hub()?;
364 hub.borrow_mut().detach(&instrument.handle)?;
365 Ok(())
366 }
367
368 pub fn acquire_control(
369 &self,
370 instrument: &NativeInstrumentHandle,
371 target: &NativeTargetHandle,
372 ) -> Result<NativeControlLease, NativeInstrumentationError> {
373 self.ensure_instrument(instrument)?;
374 self.ensure_target(target)?;
375 let hub = self.hub()?;
376 let lease = hub
377 .borrow_mut()
378 .acquire_control(&instrument.handle, &target.handle)?;
379 Ok(NativeControlLease {
380 session_id: self.session_id.clone(),
381 hub: self.hub.clone(),
382 lease,
383 })
384 }
385
386 pub fn release_control(
387 &self,
388 lease: &NativeControlLease,
389 ) -> Result<(), NativeInstrumentationError> {
390 self.ensure_lease(lease)?;
391 let hub = self.hub()?;
392 hub.borrow_mut().release_control(&lease.lease)?;
393 Ok(())
394 }
395
396 pub fn request_directive(
397 &self,
398 lease: &NativeControlLease,
399 directive: InstrumentDirective,
400 ) -> Result<(), NativeInstrumentationError> {
401 self.ensure_lease(lease)?;
402 let hub = self.hub()?;
403 hub.borrow_mut()
404 .request_directive(&lease.lease, directive)?;
405 Ok(())
406 }
407
408 fn hub(&self) -> Result<Rc<RefCell<InstrumentationHub>>, NativeInstrumentationError> {
409 self.hub
410 .upgrade()
411 .ok_or_else(|| NativeInstrumentationError::RuntimeClosed {
412 session_id: self.session_id.clone(),
413 })
414 }
415
416 fn ensure_instrument(
417 &self,
418 instrument: &NativeInstrumentHandle,
419 ) -> Result<(), NativeInstrumentationError> {
420 self.ensure_owner("instrument", &instrument.session_id, &instrument.hub)
421 }
422
423 fn ensure_target(&self, target: &NativeTargetHandle) -> Result<(), NativeInstrumentationError> {
424 self.ensure_owner("target", &target.session_id, &target.hub)
425 }
426
427 fn ensure_lease(&self, lease: &NativeControlLease) -> Result<(), NativeInstrumentationError> {
428 self.ensure_owner("control lease", &lease.session_id, &lease.hub)
429 }
430
431 fn ensure_owner(
432 &self,
433 kind: &'static str,
434 session_id: &str,
435 hub: &Weak<RefCell<InstrumentationHub>>,
436 ) -> Result<(), NativeInstrumentationError> {
437 if !Weak::ptr_eq(&self.hub, hub) {
438 return Err(NativeInstrumentationError::CrossRuntimeHandle { kind });
439 }
440 if session_id != self.session_id {
441 return Err(self.cross_session(kind, session_id));
442 }
443 self.hub()?;
444 Ok(())
445 }
446
447 fn cross_session(&self, kind: &'static str, actual: &str) -> NativeInstrumentationError {
448 NativeInstrumentationError::CrossSessionHandle {
449 kind,
450 expected: self.session_id.clone(),
451 actual: actual.into(),
452 }
453 }
454
455 fn instrument_handle(&self, handle: InstrumentHandle) -> NativeInstrumentHandle {
456 NativeInstrumentHandle {
457 session_id: self.session_id.clone(),
458 hub: self.hub.clone(),
459 handle,
460 }
461 }
462
463 fn target_handle(&self, handle: TargetHandle) -> NativeTargetHandle {
464 NativeTargetHandle {
465 session_id: self.session_id.clone(),
466 hub: self.hub.clone(),
467 handle,
468 }
469 }
470
471 fn attachment(&self, attachment: InstrumentationAttachment) -> NativeAttachment {
472 NativeAttachment {
473 instrument: self.instrument_handle(attachment.instrument),
474 target: self.target_handle(attachment.target),
475 granted_capabilities: attachment.granted_capabilities,
476 registration_order: attachment.registration_order,
477 }
478 }
479}
480
481impl NativeInstrumentHandle {
482 pub fn instrument_id(&self) -> &str {
483 self.handle.instrument_id()
484 }
485
486 pub fn generation(&self) -> u64 {
487 self.handle.generation()
488 }
489}
490
491impl NativeTargetHandle {
492 pub fn target_id(&self) -> &str {
493 self.handle.target_id()
494 }
495
496 pub fn generation(&self) -> u64 {
497 self.handle.generation()
498 }
499}
500
501impl NativeControlLease {
502 pub fn instrument_id(&self) -> &str {
503 self.lease.instrument().instrument_id()
504 }
505
506 pub fn target_id(&self) -> &str {
507 self.lease.target().target_id()
508 }
509}
510
511impl NativeAttachment {
512 pub fn instrument(&self) -> &NativeInstrumentHandle {
513 &self.instrument
514 }
515
516 pub fn target(&self) -> &NativeTargetHandle {
517 &self.target
518 }
519
520 pub fn granted_capabilities(&self) -> &BTreeSet<Capability> {
521 &self.granted_capabilities
522 }
523
524 pub fn registration_order(&self) -> u64 {
525 self.registration_order
526 }
527}
528
529#[cfg(test)]
530#[path = "native/tests.rs"]
531mod tests;