1use core::cell::RefCell;
5use core::convert::Into;
6use fidl_fuchsia_memory_attribution_plugin_common as fplugin;
7use rustc_hash::FxHashMap;
8use serde::Serialize;
9use std::collections::HashSet;
10use std::fmt::Debug;
11use summary::MemorySummary;
12
13mod name;
14pub use name::ZXName;
15pub mod digest;
16pub mod fkernel_serde;
17pub mod fplugin_serde;
18mod macros;
19pub mod summary;
20
21#[cfg(target_os = "fuchsia")]
22use {fuchsia_trace::duration, std::ffi::CStr};
23#[cfg(target_os = "fuchsia")]
24const CATEGORY_MEMORY_CAPTURE: &CStr = c"memory:capture";
25
26#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Serialize)]
28pub struct GlobalPrincipalIdentifier(pub std::num::NonZeroU64);
29
30impl GlobalPrincipalIdentifier {
31 pub fn new_for_test(value: u64) -> Self {
34 Self(std::num::NonZeroU64::new(value).unwrap())
35 }
36}
37
38impl From<fplugin::PrincipalIdentifier> for GlobalPrincipalIdentifier {
39 fn from(value: fplugin::PrincipalIdentifier) -> Self {
40 Self(std::num::NonZeroU64::new(value.id).unwrap())
41 }
42}
43
44impl From<GlobalPrincipalIdentifier> for fplugin::PrincipalIdentifier {
45 fn from(value: GlobalPrincipalIdentifier) -> fplugin::PrincipalIdentifier {
46 fplugin::PrincipalIdentifier { id: value.0.get() }
47 }
48}
49
50#[derive(Debug)]
52pub struct GlobalPrincipalIdentifierFactory {
53 next_id: std::num::NonZeroU64,
54}
55
56impl Default for GlobalPrincipalIdentifierFactory {
57 fn default() -> GlobalPrincipalIdentifierFactory {
58 GlobalPrincipalIdentifierFactory { next_id: std::num::NonZeroU64::new(1).unwrap() }
59 }
60}
61
62impl GlobalPrincipalIdentifierFactory {
63 pub fn next(&mut self) -> GlobalPrincipalIdentifier {
64 let value = GlobalPrincipalIdentifier(self.next_id);
65 self.next_id = self.next_id.checked_add(1).unwrap();
67 return value;
68 }
69}
70
71#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize)]
73pub enum PrincipalDescription {
74 Component(String),
75 Part(String),
76}
77
78impl From<fplugin::Description> for PrincipalDescription {
79 fn from(value: fplugin::Description) -> Self {
80 match value {
81 fplugin::Description::Component(s) => PrincipalDescription::Component(s),
82 fplugin::Description::Part(s) => PrincipalDescription::Part(s),
83 _ => unreachable!(),
84 }
85 }
86}
87
88impl From<PrincipalDescription> for fplugin::Description {
89 fn from(value: PrincipalDescription) -> fplugin::Description {
90 match value {
91 PrincipalDescription::Component(s) => fplugin::Description::Component(s),
92 PrincipalDescription::Part(s) => fplugin::Description::Part(s),
93 }
94 }
95}
96
97#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize)]
99pub enum PrincipalType {
100 Runnable,
101 Part,
102}
103
104impl From<fplugin::PrincipalType> for PrincipalType {
105 fn from(value: fplugin::PrincipalType) -> Self {
106 match value {
107 fplugin::PrincipalType::Runnable => PrincipalType::Runnable,
108 fplugin::PrincipalType::Part => PrincipalType::Part,
109 _ => unreachable!(),
110 }
111 }
112}
113
114impl From<PrincipalType> for fplugin::PrincipalType {
115 fn from(value: PrincipalType) -> fplugin::PrincipalType {
116 match value {
117 PrincipalType::Runnable => fplugin::PrincipalType::Runnable,
118 PrincipalType::Part => fplugin::PrincipalType::Part,
119 }
120 }
121}
122
123#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
124pub struct Principal {
126 pub identifier: GlobalPrincipalIdentifier,
128 pub description: Option<PrincipalDescription>,
129 pub principal_type: PrincipalType,
130
131 pub parent: Option<GlobalPrincipalIdentifier>,
135}
136
137impl From<fplugin::Principal> for Principal {
140 fn from(value: fplugin::Principal) -> Self {
141 Principal {
142 identifier: value.identifier.unwrap().try_into().unwrap(),
143 description: value.description.map(Into::into),
144 principal_type: value.principal_type.unwrap().into(),
145 parent: value.parent.map(|id| id.try_into().unwrap()),
146 }
147 }
148}
149
150impl From<Principal> for fplugin::Principal {
151 fn from(value: Principal) -> fplugin::Principal {
152 fplugin::Principal {
153 identifier: Some(value.identifier.into()),
154 description: value.description.map(Into::into),
155 principal_type: Some(value.principal_type.into()),
156 parent: value.parent.map(Into::into),
157 ..Default::default()
158 }
159 }
160}
161
162#[derive(Serialize)]
164pub struct InflatedPrincipal {
165 principal: Principal,
167
168 mapped_processes: Vec<u64>,
171
172 resources: Vec<u64>,
175}
176
177impl InflatedPrincipal {
178 fn new(principal: Principal) -> InflatedPrincipal {
179 InflatedPrincipal {
180 principal,
181 mapped_processes: Default::default(),
182 resources: Default::default(),
183 }
184 }
185}
186
187impl InflatedPrincipal {
188 fn name(&self) -> &str {
189 match &self.principal.description {
190 Some(PrincipalDescription::Component(component_name)) => component_name,
191 Some(PrincipalDescription::Part(part_name)) => part_name,
192 None => "?",
193 }
194 }
195}
196
197#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Serialize)]
199pub enum ClaimType {
200 Direct,
202 Indirect,
205 Child,
207}
208
209#[derive(Clone, Copy, PartialEq, Eq, Hash)]
210pub struct Koid(u64);
211
212impl From<u64> for Koid {
213 fn from(value: u64) -> Self {
214 Koid(value)
215 }
216}
217
218#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Serialize)]
224pub struct Claim {
225 pub(crate) subject: GlobalPrincipalIdentifier,
227 pub(crate) source: GlobalPrincipalIdentifier,
229 pub(crate) claim_type: ClaimType,
230}
231
232#[derive(Clone, Debug, PartialEq, Serialize)]
233pub struct Resource {
234 pub koid: u64,
235 pub name_index: usize,
236 #[serde(with = "fplugin_serde::ResourceTypeDef")]
237 pub resource_type: fplugin::ResourceType,
238}
239
240impl From<fplugin::Resource> for Resource {
241 fn from(value: fplugin::Resource) -> Self {
242 Resource {
243 koid: value.koid.unwrap(),
244 name_index: value.name_index.unwrap() as usize,
245 resource_type: value.resource_type.unwrap(),
246 }
247 }
248}
249
250impl From<Resource> for fplugin::Resource {
251 fn from(value: Resource) -> fplugin::Resource {
252 fplugin::Resource {
253 koid: Some(value.koid),
254 name_index: Some(value.name_index as u64),
255 resource_type: Some(value.resource_type),
256 ..Default::default()
257 }
258 }
259}
260
261pub struct TaggedClaim(Claim, bool);
263
264#[derive(Clone, Debug, Serialize)]
265pub struct BlobAnnotation {
266 pub manifest: String,
268 pub path: String,
270}
271
272#[derive(Clone, Debug, Serialize)]
273pub enum ResourceAnnotation {
274 Blob(BlobAnnotation),
275}
276
277#[derive(Debug, Serialize)]
279pub struct InflatedResource {
280 pub resource: Resource,
281 pub claims: HashSet<Claim>,
282 pub annotations: Vec<ResourceAnnotation>,
283}
284
285impl InflatedResource {
286 fn new(resource: Resource) -> InflatedResource {
287 InflatedResource { resource, claims: Default::default(), annotations: Default::default() }
288 }
289
290 fn children(&self) -> Vec<u64> {
291 match &self.resource.resource_type {
292 fplugin::ResourceType::Job(job) => {
293 let mut r: Vec<u64> = job.child_jobs.iter().flatten().map(|k| *k).collect();
294 r.extend(job.processes.iter().flatten().map(|k| *k));
295 r
296 }
297 fplugin::ResourceType::Process(process) => {
298 process.vmos.iter().flatten().map(|k| *k).collect()
299 }
300 fplugin::ResourceType::Vmo(_) => Vec::new(),
301 _ => todo!(),
302 }
303 }
304
305 fn process_claims(&mut self) {
315 let mut claims_by_source: FxHashMap<GlobalPrincipalIdentifier, RefCell<Vec<TaggedClaim>>> =
316 Default::default();
317 let mut self_claims = Vec::new();
318
319 for claim in self.claims.iter().cloned() {
320 if claim.source == claim.subject {
321 self_claims.push(claim);
324 } else {
325 claims_by_source
326 .entry(claim.source)
327 .or_default()
328 .borrow_mut()
329 .push(TaggedClaim(claim, false));
330 }
331 }
332
333 self.claims = self_claims.into_iter().collect();
334 for (_, claimlist_refcell) in claims_by_source.iter() {
335 let mut claimlist = claimlist_refcell.borrow_mut();
336 for tagged_claim in claimlist.iter_mut() {
337 self.claims.extend(
338 InflatedResource::process_claims_recursive(tagged_claim, &claims_by_source)
339 .into_iter(),
340 );
341 }
342 }
343 }
344
345 fn process_claims_recursive(
347 tagged_claim: &mut TaggedClaim,
348 claims: &FxHashMap<GlobalPrincipalIdentifier, RefCell<Vec<TaggedClaim>>>,
349 ) -> Vec<Claim> {
350 let claim = match tagged_claim.1 {
351 true => {
352 return vec![];
354 }
355 false => {
356 tagged_claim.1 = true;
358 tagged_claim.0
359 }
360 };
361 let subject = &claim.subject;
362 let mut subject_claims = match claims.get(subject) {
364 Some(value_ref) => {
365 value_ref.try_borrow_mut().expect("Claims form a cycle, this is not supported")
370 }
371 None => {
372 return vec![claim];
374 }
375 };
376 let mut leaves = vec![];
377 for subject_claim in subject_claims.iter_mut() {
378 leaves.append(&mut InflatedResource::process_claims_recursive(subject_claim, claims));
379 }
380 leaves
381 }
382}
383
384#[derive(Clone, Serialize)]
385pub struct Attribution {
387 pub source: GlobalPrincipalIdentifier,
389 pub subject: GlobalPrincipalIdentifier,
391 pub resources: Vec<ResourceReference>,
393}
394
395impl From<fplugin::Attribution> for Attribution {
396 fn from(value: fplugin::Attribution) -> Attribution {
397 Attribution {
398 source: value.source.unwrap().into(),
399 subject: value.subject.unwrap().into(),
400 resources: value.resources.unwrap().into_iter().map(|r| r.into()).collect(),
401 }
402 }
403}
404
405impl From<Attribution> for fplugin::Attribution {
406 fn from(value: Attribution) -> fplugin::Attribution {
407 fplugin::Attribution {
408 source: Some(value.source.into()),
409 subject: Some(value.subject.into()),
410 resources: Some(value.resources.into_iter().map(|r| r.into()).collect()),
411 ..Default::default()
412 }
413 }
414}
415
416#[derive(Clone, Copy, Serialize)]
417pub enum ResourceReference {
420 KernelObject(u64),
425
426 ProcessMapped {
428 process: u64,
430
431 base: u64,
433
434 len: u64,
436
437 hint_skip_handle_table: bool,
440 },
441}
442
443impl From<fplugin::ResourceReference> for ResourceReference {
444 fn from(value: fplugin::ResourceReference) -> ResourceReference {
445 match value {
446 fplugin::ResourceReference::KernelObject(ko) => ResourceReference::KernelObject(ko),
447 fplugin::ResourceReference::ProcessMapped(fplugin::ProcessMapped {
448 process,
449 base,
450 len,
451 hint_skip_handle_table,
452 }) => ResourceReference::ProcessMapped { process, base, len, hint_skip_handle_table },
453 _ => unimplemented!(),
454 }
455 }
456}
457
458impl From<ResourceReference> for fplugin::ResourceReference {
459 fn from(value: ResourceReference) -> fplugin::ResourceReference {
460 match value {
461 ResourceReference::KernelObject(ko) => fplugin::ResourceReference::KernelObject(ko),
462 ResourceReference::ProcessMapped { process, base, len, hint_skip_handle_table } => {
463 fplugin::ResourceReference::ProcessMapped(fplugin::ProcessMapped {
464 process,
465 base,
466 len,
467 hint_skip_handle_table,
468 })
469 }
470 }
471 }
472}
473
474pub struct AttributionData {
477 pub principals_vec: Vec<Principal>,
478 pub resources_vec: Vec<Resource>,
479 pub resource_names: Vec<ZXName>,
480 pub attributions: Vec<Attribution>,
481}
482
483pub trait AttributionDataProvider: Send + Sync {
484 fn get_attribution_data(&self) -> Result<AttributionData, anyhow::Error>;
486}
487
488pub struct ProcessedAttributionData {
491 pub principals: FxHashMap<GlobalPrincipalIdentifier, InflatedPrincipal>,
492 pub resources: FxHashMap<u64, InflatedResource>,
493 pub resource_names: Vec<ZXName>,
494}
495
496impl ProcessedAttributionData {
497 fn new(
498 principals: FxHashMap<GlobalPrincipalIdentifier, InflatedPrincipal>,
499 resources: FxHashMap<u64, InflatedResource>,
500 resource_names: Vec<ZXName>,
501 ) -> Self {
502 Self { principals, resources, resource_names }
503 }
504
505 pub fn summary(&self) -> MemorySummary {
507 #[cfg(target_os = "fuchsia")]
508 duration!(CATEGORY_MEMORY_CAPTURE, c"ProcessedAttributionData::summary");
509 MemorySummary::build(&self.principals, &self.resources, &self.resource_names)
510 }
511}
512
513pub fn attribute_vmos(attribution_data: AttributionData) -> ProcessedAttributionData {
515 #[cfg(target_os = "fuchsia")]
516 duration!(CATEGORY_MEMORY_CAPTURE, c"attribute_vmos");
517
518 let mut principals: Vec<InflatedPrincipal> =
523 attribution_data.principals_vec.into_iter().map(InflatedPrincipal::new).collect();
524
525 let principal_by_id: FxHashMap<GlobalPrincipalIdentifier, usize> =
526 principals.iter().enumerate().map(|(idx, p)| (p.principal.identifier, idx)).collect();
527
528 let mut resources: Vec<InflatedResource> =
530 attribution_data.resources_vec.into_iter().map(InflatedResource::new).collect();
531
532 let resource_by_koid: FxHashMap<u64, usize> =
533 resources.iter().enumerate().map(|(idx, r)| (r.resource.koid, idx)).collect();
534
535 let mut matched_vmo_indices: Vec<usize> = Vec::new();
537
538 for attribution in attribution_data.attributions {
540 for resource in attribution.resources {
541 match resource {
542 ResourceReference::KernelObject(koid) => {
543 if let Some(&r_idx) = resource_by_koid.get(&koid) {
544 resources[r_idx].claims.insert(Claim {
545 source: attribution.source,
546 subject: attribution.subject,
547 claim_type: ClaimType::Direct,
548 });
549 }
550 }
551 ResourceReference::ProcessMapped {
552 process,
553 base,
554 len,
555 hint_skip_handle_table: _,
556 } => {
557 if let Some(&principal_idx) = principal_by_id.get(&attribution.subject) {
558 principals[principal_idx].mapped_processes.push(process);
559 }
560 let Some(&process_idx) = resource_by_koid.get(&process) else {
561 continue;
562 };
563 matched_vmo_indices.clear();
564 if let fplugin::ResourceType::Process(process_data) =
565 &resources[process_idx].resource.resource_type
566 {
567 if let Some(mappings) = &process_data.mappings {
568 let start_idx =
572 mappings.partition_point(|m| m.address_base.unwrap_or(0) < base);
573 let end_bound = base.saturating_add(len);
574 for mapping in &mappings[start_idx..] {
575 let Some(mapping_base) = mapping.address_base else {
576 continue;
577 };
578 if mapping_base >= end_bound {
580 break;
581 }
582 let Some(mapping_size) = mapping.size else {
583 continue;
584 };
585 if mapping_base.saturating_add(mapping_size) <= end_bound {
588 if let Some(vmo_koid) = mapping.vmo {
589 if let Some(&vmo_idx) = resource_by_koid.get(&vmo_koid) {
590 matched_vmo_indices.push(vmo_idx);
591 }
592 }
593 }
594 }
595 }
596 }
597 for &vmo_idx in &matched_vmo_indices {
598 resources[vmo_idx].claims.insert(Claim {
599 source: attribution.source,
600 subject: attribution.subject,
601 claim_type: ClaimType::Direct,
602 });
603 }
604 }
605 }
606 }
607 }
608
609 for res_idx in 0..resources.len() {
614 if !resources[res_idx].claims.iter().any(|c| c.claim_type == ClaimType::Direct) {
615 continue;
617 }
618
619 let propagated_claims: Vec<Claim> = resources[res_idx]
620 .claims
621 .iter()
622 .filter(|c| c.claim_type == ClaimType::Direct)
623 .map(|claim| Claim {
624 source: claim.source,
625 subject: claim.subject,
626 claim_type: ClaimType::Indirect,
627 })
628 .collect();
629 let mut frontier = Vec::new();
630 frontier.extend(resources[res_idx].children());
631 while let Some(child) = frontier.pop() {
632 let Some(&child_idx) = resource_by_koid.get(&child) else {
633 continue;
637 };
638 if resources[child_idx].claims.iter().any(|c| c.claim_type == ClaimType::Direct) {
639 continue;
641 }
642 resources[child_idx].claims.extend(propagated_claims.iter().cloned());
643 frontier.extend(resources[child_idx].children());
644 }
645 }
646
647 for resource in &mut resources {
648 resource.process_claims();
649 }
650
651 let mut ancestors_buf: Vec<u64> = Vec::with_capacity(8);
653 let mut child_claims_buf: Vec<Claim> = Vec::new();
654
655 for res_idx in 0..resources.len() {
658 let resource_koid = resources[res_idx].resource.koid;
659 if let fplugin::ResourceType::Vmo(vmo) = &resources[res_idx].resource.resource_type {
660 ancestors_buf.clear();
661 ancestors_buf.push(resource_koid);
662
663 if vmo.total_populated_bytes.unwrap_or_default() == 0 {
670 let mut current_parent = vmo.parent;
671 child_claims_buf.clear();
672 for c in &resources[res_idx].claims {
673 child_claims_buf.push(Claim {
674 subject: c.subject,
675 source: c.source,
676 claim_type: ClaimType::Child,
677 });
678 }
679 while let Some(parent_koid) = current_parent {
683 if parent_koid == 0 {
684 panic!("Parent is not None but 0.");
685 }
686 if parent_koid == resource_koid {
690 break;
691 }
692 ancestors_buf.push(parent_koid);
693 let Some(&parent_idx) = resource_by_koid.get(&parent_koid) else {
694 break;
695 };
696 resources[parent_idx].claims.extend(child_claims_buf.iter().cloned());
697 current_parent = match &resources[parent_idx].resource.resource_type {
698 fplugin::ResourceType::Job(_) => panic!("This should not happen"),
699 fplugin::ResourceType::Process(_) => panic!("This should not happen"),
700 fplugin::ResourceType::Vmo(current_vmo) => current_vmo.parent,
701 _ => unimplemented!(),
702 };
703 }
704 }
705
706 for claim in &resources[res_idx].claims {
707 if let Some(&p_idx) = principal_by_id.get(&claim.subject) {
708 principals[p_idx].resources.extend_from_slice(&ancestors_buf);
709 }
710 }
711 } else if let fplugin::ResourceType::Process(_) = &resources[res_idx].resource.resource_type
712 {
713 for claim in &resources[res_idx].claims {
714 if let Some(&p_idx) = principal_by_id.get(&claim.subject) {
715 principals[p_idx].resources.push(resource_koid);
716 }
717 }
718 }
719 }
720
721 for p in &mut principals {
723 if !p.resources.is_empty() {
724 p.resources.sort_unstable();
725 p.resources.dedup();
726 }
727 }
728
729 let principals_map = principals.into_iter().map(|p| (p.principal.identifier, p)).collect();
730 let resources_map = resources.into_iter().map(|r| (r.resource.koid, r)).collect();
731
732 ProcessedAttributionData::new(principals_map, resources_map, attribution_data.resource_names)
733}
734
735#[cfg(test)]
736mod tests {
737 use super::*;
738 use std::collections::HashMap;
739 use summary::{PrincipalSummary, VmoSummary};
740
741 #[test]
742 fn test_gather_resources() {
743 let resource_names = vec![
767 ZXName::from_string_lossy("root_job"),
768 ZXName::from_string_lossy("root_process"),
769 ZXName::from_string_lossy("root_vmo"),
770 ZXName::from_string_lossy("shared_vmo"),
771 ZXName::from_string_lossy("runner_job"),
772 ZXName::from_string_lossy("runner_process"),
773 ZXName::from_string_lossy("runner_vmo"),
774 ZXName::from_string_lossy("component_vmo"),
775 ZXName::from_string_lossy("component_2_job"),
776 ZXName::from_string_lossy("2_process"),
777 ZXName::from_string_lossy("2_vmo"),
778 ZXName::from_string_lossy("2_vmo_parent"),
779 ZXName::from_string_lossy("component_vmo_mapped"),
780 ZXName::from_string_lossy("component_vmo_mapped2"),
781 ];
782
783 let attributions = vec![
784 fplugin::Attribution {
785 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
786 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
787 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
788 ..Default::default()
789 },
790 fplugin::Attribution {
791 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
792 subject: Some(fplugin::PrincipalIdentifier { id: 2 }),
793 resources: Some(vec![fplugin::ResourceReference::KernelObject(1004)]),
794 ..Default::default()
795 },
796 fplugin::Attribution {
797 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
798 subject: Some(fplugin::PrincipalIdentifier { id: 3 }),
799 resources: Some(vec![fplugin::ResourceReference::KernelObject(1008)]),
800 ..Default::default()
801 },
802 fplugin::Attribution {
803 source: Some(fplugin::PrincipalIdentifier { id: 2 }),
804 subject: Some(fplugin::PrincipalIdentifier { id: 4 }),
805 resources: Some(vec![
806 fplugin::ResourceReference::KernelObject(1007),
807 fplugin::ResourceReference::ProcessMapped(fplugin::ProcessMapped {
808 process: 1005,
809 base: 1024,
810 len: 1024,
811 hint_skip_handle_table: false,
812 }),
813 ]),
814 ..Default::default()
815 },
816 ]
817 .into_iter()
818 .map(|a| a.into())
819 .collect();
820
821 let principals = vec![
822 fplugin::Principal {
823 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
824 description: Some(fplugin::Description::Component("component_manager".to_owned())),
825 principal_type: Some(fplugin::PrincipalType::Runnable),
826 parent: None,
827 ..Default::default()
828 },
829 fplugin::Principal {
830 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
831 description: Some(fplugin::Description::Component("runner".to_owned())),
832 principal_type: Some(fplugin::PrincipalType::Runnable),
833 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
834 ..Default::default()
835 },
836 fplugin::Principal {
837 identifier: Some(fplugin::PrincipalIdentifier { id: 3 }),
838 description: Some(fplugin::Description::Component("component 3".to_owned())),
839 principal_type: Some(fplugin::PrincipalType::Runnable),
840 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
841 ..Default::default()
842 },
843 fplugin::Principal {
844 identifier: Some(fplugin::PrincipalIdentifier { id: 4 }),
845 description: Some(fplugin::Description::Component("component 4".to_owned())),
846 principal_type: Some(fplugin::PrincipalType::Runnable),
847 parent: Some(fplugin::PrincipalIdentifier { id: 2 }),
848 ..Default::default()
849 },
850 ]
851 .into_iter()
852 .map(|p| p.into())
853 .collect();
854
855 let resources = vec![
856 fplugin::Resource {
857 koid: Some(1000),
858 name_index: Some(0),
859 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
860 child_jobs: Some(vec![1004, 1008]),
861 processes: Some(vec![1001]),
862 ..Default::default()
863 })),
864 ..Default::default()
865 },
866 fplugin::Resource {
867 koid: Some(1001),
868 name_index: Some(1),
869 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
870 vmos: Some(vec![1002, 1003]),
871 mappings: None,
872 ..Default::default()
873 })),
874 ..Default::default()
875 },
876 fplugin::Resource {
877 koid: Some(1002),
878 name_index: Some(2),
879 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
880 private_committed_bytes: Some(1024),
881 private_populated_bytes: Some(2048),
882 scaled_committed_bytes: Some(1024),
883 scaled_populated_bytes: Some(2048),
884 total_committed_bytes: Some(1024),
885 total_populated_bytes: Some(2048),
886 ..Default::default()
887 })),
888 ..Default::default()
889 },
890 fplugin::Resource {
891 koid: Some(1003),
892 name_index: Some(3),
893 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
894 private_committed_bytes: Some(1024),
895 private_populated_bytes: Some(2048),
896 scaled_committed_bytes: Some(1024),
897 scaled_populated_bytes: Some(2048),
898 total_committed_bytes: Some(1024),
899 total_populated_bytes: Some(2048),
900 ..Default::default()
901 })),
902 ..Default::default()
903 },
904 fplugin::Resource {
905 koid: Some(1004),
906 name_index: Some(4),
907 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
908 child_jobs: Some(vec![]),
909 processes: Some(vec![1005]),
910 ..Default::default()
911 })),
912 ..Default::default()
913 },
914 fplugin::Resource {
915 koid: Some(1005),
916 name_index: Some(5),
917 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
918 vmos: Some(vec![1006, 1007, 1012]),
919 mappings: Some(vec![
920 fplugin::Mapping {
921 vmo: Some(1006),
922 address_base: Some(0),
923 size: Some(512),
924 ..Default::default()
925 },
926 fplugin::Mapping {
927 vmo: Some(1012),
928 address_base: Some(1024),
929 size: Some(512),
930 ..Default::default()
931 },
932 fplugin::Mapping {
933 vmo: Some(1013),
934 address_base: Some(1536),
935 size: Some(512),
936 ..Default::default()
937 },
938 fplugin::Mapping {
939 vmo: Some(1006),
940 address_base: Some(2048),
941 size: Some(512),
942 ..Default::default()
943 },
944 ]),
945 ..Default::default()
946 })),
947 ..Default::default()
948 },
949 fplugin::Resource {
950 koid: Some(1006),
951 name_index: Some(6),
952 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
953 private_committed_bytes: Some(1024),
954 private_populated_bytes: Some(2048),
955 scaled_committed_bytes: Some(1024),
956 scaled_populated_bytes: Some(2048),
957 total_committed_bytes: Some(1024),
958 total_populated_bytes: Some(2048),
959 ..Default::default()
960 })),
961 ..Default::default()
962 },
963 fplugin::Resource {
964 koid: Some(1007),
965 name_index: Some(7),
966 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
967 private_committed_bytes: Some(128),
968 private_populated_bytes: Some(256),
969 scaled_committed_bytes: Some(128),
970 scaled_populated_bytes: Some(256),
971 total_committed_bytes: Some(128),
972 total_populated_bytes: Some(256),
973 ..Default::default()
974 })),
975 ..Default::default()
976 },
977 fplugin::Resource {
978 koid: Some(1008),
979 name_index: Some(8),
980 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
981 child_jobs: Some(vec![]),
982 processes: Some(vec![1009]),
983 ..Default::default()
984 })),
985 ..Default::default()
986 },
987 fplugin::Resource {
988 koid: Some(1009),
989 name_index: Some(9),
990 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
991 vmos: Some(vec![1010, 1003]),
992 mappings: None,
993 ..Default::default()
994 })),
995 ..Default::default()
996 },
997 fplugin::Resource {
998 koid: Some(1010),
999 name_index: Some(10),
1000 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1001 parent: Some(1011),
1002 private_committed_bytes: Some(1024),
1003 private_populated_bytes: Some(2048),
1004 scaled_committed_bytes: Some(1024),
1005 scaled_populated_bytes: Some(2048),
1006 total_committed_bytes: Some(1024),
1007 total_populated_bytes: Some(2048),
1008 ..Default::default()
1009 })),
1010 ..Default::default()
1011 },
1012 fplugin::Resource {
1013 koid: Some(1011),
1014 name_index: Some(11),
1015 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1016 private_committed_bytes: Some(1024),
1017 private_populated_bytes: Some(2048),
1018 scaled_committed_bytes: Some(1024),
1019 scaled_populated_bytes: Some(2048),
1020 total_committed_bytes: Some(1024),
1021 total_populated_bytes: Some(2048),
1022 ..Default::default()
1023 })),
1024 ..Default::default()
1025 },
1026 fplugin::Resource {
1027 koid: Some(1012),
1028 name_index: Some(12),
1029 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1030 private_committed_bytes: Some(1024),
1031 private_populated_bytes: Some(2048),
1032 scaled_committed_bytes: Some(1024),
1033 scaled_populated_bytes: Some(2048),
1034 total_committed_bytes: Some(1024),
1035 total_populated_bytes: Some(2048),
1036 ..Default::default()
1037 })),
1038 ..Default::default()
1039 },
1040 fplugin::Resource {
1041 koid: Some(1013),
1042 name_index: Some(13),
1043 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1044 private_committed_bytes: Some(1024),
1045 private_populated_bytes: Some(2048),
1046 scaled_committed_bytes: Some(1024),
1047 scaled_populated_bytes: Some(2048),
1048 total_committed_bytes: Some(1024),
1049 total_populated_bytes: Some(2048),
1050 ..Default::default()
1051 })),
1052 ..Default::default()
1053 },
1054 ]
1055 .into_iter()
1056 .map(|r| r.into())
1057 .collect();
1058
1059 let output = attribute_vmos(AttributionData {
1060 principals_vec: principals,
1061 resources_vec: resources,
1062 resource_names,
1063 attributions,
1064 })
1065 .summary();
1066
1067 assert_eq!(output.unclaimed, 2048);
1068 assert_eq!(output.principals.len(), 4);
1069
1070 let principals: HashMap<u64, PrincipalSummary> =
1071 output.principals.into_iter().map(|p| (p.id, p)).collect();
1072
1073 assert_eq!(
1074 principals.get(&1).unwrap(),
1075 &PrincipalSummary {
1076 id: 1,
1077 name: "component_manager".to_owned(),
1078 principal_type: "R".to_owned(),
1079 committed_private: 1024,
1080 committed_scaled: 1536.0,
1081 committed_total: 2048,
1082 populated_private: 2048,
1083 populated_scaled: 3072.0,
1084 populated_total: 4096,
1085 attributor: None,
1086 processes: vec!["root_process (1001)".to_owned()],
1087 vmos: vec![
1088 (
1089 ZXName::from_string_lossy("root_vmo"),
1090 VmoSummary {
1091 count: 1,
1092 committed_private: 1024,
1093 committed_scaled: 1024.0,
1094 committed_total: 1024,
1095 populated_private: 2048,
1096 populated_scaled: 2048.0,
1097 populated_total: 2048,
1098 ..Default::default()
1099 }
1100 ),
1101 (
1102 ZXName::from_string_lossy("shared_vmo"),
1103 VmoSummary {
1104 count: 1,
1105 committed_private: 0,
1106 committed_scaled: 512.0,
1107 committed_total: 1024,
1108 populated_private: 0,
1109 populated_scaled: 1024.0,
1110 populated_total: 2048,
1111 ..Default::default()
1112 }
1113 )
1114 ]
1115 .into_iter()
1116 .collect(),
1117 }
1118 );
1119
1120 assert_eq!(
1121 principals.get(&2).unwrap(),
1122 &PrincipalSummary {
1123 id: 2,
1124 name: "runner".to_owned(),
1125 principal_type: "R".to_owned(),
1126 committed_private: 1024,
1127 committed_scaled: 1024.0,
1128 committed_total: 1024,
1129 populated_private: 2048,
1130 populated_scaled: 2048.0,
1131 populated_total: 2048,
1132 attributor: Some("component_manager".to_owned()),
1133 processes: vec!["runner_process (1005)".to_owned()],
1134 vmos: vec![(
1135 ZXName::from_string_lossy("runner_vmo"),
1136 VmoSummary {
1137 count: 1,
1138 committed_private: 1024,
1139 committed_scaled: 1024.0,
1140 committed_total: 1024,
1141 populated_private: 2048,
1142 populated_scaled: 2048.0,
1143 populated_total: 2048,
1144 ..Default::default()
1145 }
1146 )]
1147 .into_iter()
1148 .collect(),
1149 }
1150 );
1151
1152 assert_eq!(
1153 principals.get(&3).unwrap(),
1154 &PrincipalSummary {
1155 id: 3,
1156 name: "component 3".to_owned(),
1157 principal_type: "R".to_owned(),
1158 committed_private: 1024,
1159 committed_scaled: 1536.0,
1160 committed_total: 2048,
1161 populated_private: 2048,
1162 populated_scaled: 3072.0,
1163 populated_total: 4096,
1164 attributor: Some("component_manager".to_owned()),
1165 processes: vec!["2_process (1009)".to_owned()],
1166 vmos: vec![
1167 (
1168 ZXName::from_string_lossy("shared_vmo"),
1169 VmoSummary {
1170 count: 1,
1171 committed_private: 0,
1172 committed_scaled: 512.0,
1173 committed_total: 1024,
1174 populated_private: 0,
1175 populated_scaled: 1024.0,
1176 populated_total: 2048,
1177 ..Default::default()
1178 }
1179 ),
1180 (
1181 ZXName::from_string_lossy("2_vmo"),
1182 VmoSummary {
1183 count: 1,
1184 committed_private: 1024,
1185 committed_scaled: 1024.0,
1186 committed_total: 1024,
1187 populated_private: 2048,
1188 populated_scaled: 2048.0,
1189 populated_total: 2048,
1190 ..Default::default()
1191 }
1192 )
1193 ]
1194 .into_iter()
1195 .collect(),
1196 }
1197 );
1198
1199 assert_eq!(
1200 principals.get(&4).unwrap(),
1201 &PrincipalSummary {
1202 id: 4,
1203 name: "component 4".to_owned(),
1204 principal_type: "R".to_owned(),
1205 committed_private: 2176,
1206 committed_scaled: 2176.0,
1207 committed_total: 2176,
1208 populated_private: 4352,
1209 populated_scaled: 4352.0,
1210 populated_total: 4352,
1211 attributor: Some("runner".to_owned()),
1212 processes: vec!["runner_process (1005)".to_owned()],
1213 vmos: vec![
1214 (
1215 ZXName::from_string_lossy("component_vmo"),
1216 VmoSummary {
1217 count: 1,
1218 committed_private: 128,
1219 committed_scaled: 128.0,
1220 committed_total: 128,
1221 populated_private: 256,
1222 populated_scaled: 256.0,
1223 populated_total: 256,
1224 ..Default::default()
1225 }
1226 ),
1227 (
1228 ZXName::from_string_lossy("component_vmo_mapped"),
1229 VmoSummary {
1230 count: 1,
1231 committed_private: 1024,
1232 committed_scaled: 1024.0,
1233 committed_total: 1024,
1234 populated_private: 2048,
1235 populated_scaled: 2048.0,
1236 populated_total: 2048,
1237 ..Default::default()
1238 }
1239 ),
1240 (
1241 ZXName::from_string_lossy("component_vmo_mapped2"),
1242 VmoSummary {
1243 count: 1,
1244 committed_private: 1024,
1245 committed_scaled: 1024.0,
1246 committed_total: 1024,
1247 populated_private: 2048,
1248 populated_scaled: 2048.0,
1249 populated_total: 2048,
1250 ..Default::default()
1251 }
1252 )
1253 ]
1254 .into_iter()
1255 .collect(),
1256 }
1257 );
1258 }
1259
1260 #[test]
1261 fn test_reshare_resources() {
1262 let resource_names = vec![
1276 ZXName::from_string_lossy("root_job"),
1277 ZXName::from_string_lossy("component_job"),
1278 ZXName::from_string_lossy("component_process"),
1279 ZXName::from_string_lossy("component_vmo"),
1280 ];
1281 let attributions = vec![
1282 fplugin::Attribution {
1283 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
1284 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
1285 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
1286 ..Default::default()
1287 },
1288 fplugin::Attribution {
1289 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
1290 subject: Some(fplugin::PrincipalIdentifier { id: 2 }),
1291 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1292 ..Default::default()
1293 },
1294 fplugin::Attribution {
1295 source: Some(fplugin::PrincipalIdentifier { id: 2 }),
1296 subject: Some(fplugin::PrincipalIdentifier { id: 3 }),
1297 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1298 ..Default::default()
1299 },
1300 ]
1301 .into_iter()
1302 .map(|a| a.into())
1303 .collect();
1304 let principals = vec![
1305 fplugin::Principal {
1306 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
1307 description: Some(fplugin::Description::Component("component_manager".to_owned())),
1308 principal_type: Some(fplugin::PrincipalType::Runnable),
1309 parent: None,
1310 ..Default::default()
1311 },
1312 fplugin::Principal {
1313 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
1314 description: Some(fplugin::Description::Component("component 2".to_owned())),
1315 principal_type: Some(fplugin::PrincipalType::Runnable),
1316 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
1317 ..Default::default()
1318 },
1319 fplugin::Principal {
1320 identifier: Some(fplugin::PrincipalIdentifier { id: 3 }),
1321 description: Some(fplugin::Description::Component("component 3".to_owned())),
1322 principal_type: Some(fplugin::PrincipalType::Runnable),
1323 parent: Some(fplugin::PrincipalIdentifier { id: 2 }),
1324 ..Default::default()
1325 },
1326 ]
1327 .into_iter()
1328 .map(|p| p.into())
1329 .collect();
1330
1331 let resources = vec![
1332 fplugin::Resource {
1333 koid: Some(1000),
1334 name_index: Some(0),
1335 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1336 child_jobs: Some(vec![1001]),
1337 processes: Some(vec![]),
1338 ..Default::default()
1339 })),
1340 ..Default::default()
1341 },
1342 fplugin::Resource {
1343 koid: Some(1001),
1344 name_index: Some(1),
1345 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1346 child_jobs: Some(vec![]),
1347 processes: Some(vec![1002]),
1348 ..Default::default()
1349 })),
1350 ..Default::default()
1351 },
1352 fplugin::Resource {
1353 koid: Some(1002),
1354 name_index: Some(2),
1355 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1356 vmos: Some(vec![1003]),
1357 mappings: None,
1358 ..Default::default()
1359 })),
1360 ..Default::default()
1361 },
1362 fplugin::Resource {
1363 koid: Some(1003),
1364 name_index: Some(3),
1365 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1366 private_committed_bytes: Some(1024),
1367 private_populated_bytes: Some(2048),
1368 scaled_committed_bytes: Some(1024),
1369 scaled_populated_bytes: Some(2048),
1370 total_committed_bytes: Some(1024),
1371 total_populated_bytes: Some(2048),
1372 ..Default::default()
1373 })),
1374 ..Default::default()
1375 },
1376 ]
1377 .into_iter()
1378 .map(|r| r.into())
1379 .collect();
1380
1381 let output = attribute_vmos(AttributionData {
1382 principals_vec: principals,
1383 resources_vec: resources,
1384 resource_names,
1385 attributions,
1386 })
1387 .summary();
1388
1389 assert_eq!(output.unclaimed, 0);
1390 assert_eq!(output.principals.len(), 3);
1391
1392 let principals: HashMap<u64, PrincipalSummary> =
1393 output.principals.into_iter().map(|p| (p.id, p)).collect();
1394
1395 assert_eq!(
1396 principals.get(&1).unwrap(),
1397 &PrincipalSummary {
1398 id: 1,
1399 name: "component_manager".to_owned(),
1400 principal_type: "R".to_owned(),
1401 committed_private: 0,
1402 committed_scaled: 0.0,
1403 committed_total: 0,
1404 populated_private: 0,
1405 populated_scaled: 0.0,
1406 populated_total: 0,
1407 attributor: None,
1408 processes: vec![],
1409 vmos: vec![].into_iter().collect(),
1410 }
1411 );
1412
1413 assert_eq!(
1414 principals.get(&2).unwrap(),
1415 &PrincipalSummary {
1416 id: 2,
1417 name: "component 2".to_owned(),
1418 principal_type: "R".to_owned(),
1419 committed_private: 0,
1420 committed_scaled: 0.0,
1421 committed_total: 0,
1422 populated_private: 0,
1423 populated_scaled: 0.0,
1424 populated_total: 0,
1425 attributor: Some("component_manager".to_owned()),
1426 processes: vec![],
1427 vmos: vec![].into_iter().collect(),
1428 }
1429 );
1430
1431 assert_eq!(
1432 principals.get(&3).unwrap(),
1433 &PrincipalSummary {
1434 id: 3,
1435 name: "component 3".to_owned(),
1436 principal_type: "R".to_owned(),
1437 committed_private: 1024,
1438 committed_scaled: 1024.0,
1439 committed_total: 1024,
1440 populated_private: 2048,
1441 populated_scaled: 2048.0,
1442 populated_total: 2048,
1443 attributor: Some("component 2".to_owned()),
1444 processes: vec!["component_process (1002)".to_owned()],
1445 vmos: vec![(
1446 ZXName::from_string_lossy("component_vmo"),
1447 VmoSummary {
1448 count: 1,
1449 committed_private: 1024,
1450 committed_scaled: 1024.0,
1451 committed_total: 1024,
1452 populated_private: 2048,
1453 populated_scaled: 2048.0,
1454 populated_total: 2048,
1455 ..Default::default()
1456 }
1457 ),]
1458 .into_iter()
1459 .collect(),
1460 }
1461 );
1462 }
1463
1464 #[test]
1465 fn test_conversions() {
1466 let plugin_principal = fplugin::Principal {
1467 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
1468 description: Some(fplugin::Description::Component("component_manager".to_owned())),
1469 principal_type: Some(fplugin::PrincipalType::Runnable),
1470 parent: None,
1471 ..Default::default()
1472 };
1473
1474 let data_principal: Principal = plugin_principal.clone().into();
1475
1476 assert_eq!(plugin_principal, data_principal.into());
1477
1478 let plugin_resources = vec![
1479 fplugin::Resource {
1480 koid: Some(1000),
1481 name_index: Some(0),
1482 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1483 child_jobs: Some(vec![1004, 1008]),
1484 processes: Some(vec![1001]),
1485 ..Default::default()
1486 })),
1487 ..Default::default()
1488 },
1489 fplugin::Resource {
1490 koid: Some(1001),
1491 name_index: Some(1),
1492 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1493 vmos: Some(vec![1002, 1003]),
1494 mappings: None,
1495 ..Default::default()
1496 })),
1497 ..Default::default()
1498 },
1499 fplugin::Resource {
1500 koid: Some(1002),
1501 name_index: Some(2),
1502 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1503 private_committed_bytes: Some(1024),
1504 private_populated_bytes: Some(2048),
1505 scaled_committed_bytes: Some(1024),
1506 scaled_populated_bytes: Some(2048),
1507 total_committed_bytes: Some(1024),
1508 total_populated_bytes: Some(2048),
1509 ..Default::default()
1510 })),
1511 ..Default::default()
1512 },
1513 fplugin::Resource {
1514 koid: Some(1005),
1515 name_index: Some(5),
1516 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1517 vmos: Some(vec![1006, 1007, 1012]),
1518 mappings: Some(vec![
1519 fplugin::Mapping {
1520 vmo: Some(1006),
1521 address_base: Some(0),
1522 size: Some(512),
1523 ..Default::default()
1524 },
1525 fplugin::Mapping {
1526 vmo: Some(1012),
1527 address_base: Some(1024),
1528 size: Some(512),
1529 ..Default::default()
1530 },
1531 ]),
1532 ..Default::default()
1533 })),
1534 ..Default::default()
1535 },
1536 ];
1537
1538 let data_resources: Vec<Resource> =
1539 plugin_resources.iter().cloned().map(|r| r.into()).collect();
1540
1541 let actual_resources: Vec<fplugin::Resource> =
1542 data_resources.into_iter().map(|r| r.into()).collect();
1543
1544 assert_eq!(plugin_resources, actual_resources);
1545 }
1546
1547 #[test]
1548 fn test_vmo_reference() {
1549 let resource_names = vec![
1564 name::ZXName::from_string_lossy("root_job"),
1565 name::ZXName::from_string_lossy("component_job"),
1566 name::ZXName::from_string_lossy("component_process"),
1567 name::ZXName::from_string_lossy("component_vmo"),
1568 name::ZXName::from_string_lossy("component_vmo_parent"),
1569 ];
1570 let attributions = vec![
1571 fplugin::Attribution {
1572 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
1573 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
1574 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
1575 ..Default::default()
1576 },
1577 fplugin::Attribution {
1578 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
1579 subject: Some(fplugin::PrincipalIdentifier { id: 2 }),
1580 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1581 ..Default::default()
1582 },
1583 ]
1584 .into_iter()
1585 .map(|a| a.into())
1586 .collect();
1587 let principals = vec![
1588 fplugin::Principal {
1589 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
1590 description: Some(fplugin::Description::Component("component_manager".to_owned())),
1591 principal_type: Some(fplugin::PrincipalType::Runnable),
1592 parent: None,
1593 ..Default::default()
1594 },
1595 fplugin::Principal {
1596 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
1597 description: Some(fplugin::Description::Component("component 2".to_owned())),
1598 principal_type: Some(fplugin::PrincipalType::Runnable),
1599 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
1600 ..Default::default()
1601 },
1602 ]
1603 .into_iter()
1604 .map(|p| p.into())
1605 .collect();
1606
1607 let resources = vec![
1608 fplugin::Resource {
1609 koid: Some(1000),
1610 name_index: Some(0),
1611 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1612 child_jobs: Some(vec![1001]),
1613 processes: Some(vec![]),
1614 ..Default::default()
1615 })),
1616 ..Default::default()
1617 },
1618 fplugin::Resource {
1619 koid: Some(1001),
1620 name_index: Some(1),
1621 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1622 child_jobs: Some(vec![]),
1623 processes: Some(vec![1002]),
1624 ..Default::default()
1625 })),
1626 ..Default::default()
1627 },
1628 fplugin::Resource {
1629 koid: Some(1002),
1630 name_index: Some(2),
1631 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1632 vmos: Some(vec![1003]),
1633 mappings: None,
1634 ..Default::default()
1635 })),
1636 ..Default::default()
1637 },
1638 fplugin::Resource {
1639 koid: Some(1003),
1640 name_index: Some(3),
1641 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1642 parent: Some(1004),
1643 private_committed_bytes: Some(0),
1644 private_populated_bytes: Some(0),
1645 scaled_committed_bytes: Some(0),
1646 scaled_populated_bytes: Some(0),
1647 total_committed_bytes: Some(0),
1648 total_populated_bytes: Some(0),
1649 ..Default::default()
1650 })),
1651 ..Default::default()
1652 },
1653 fplugin::Resource {
1654 koid: Some(1004),
1655 name_index: Some(4),
1656 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1657 private_committed_bytes: Some(1024),
1658 private_populated_bytes: Some(2048),
1659 scaled_committed_bytes: Some(1024),
1660 scaled_populated_bytes: Some(2048),
1661 total_committed_bytes: Some(1024),
1662 total_populated_bytes: Some(2048),
1663 ..Default::default()
1664 })),
1665 ..Default::default()
1666 },
1667 ]
1668 .into_iter()
1669 .map(|r| r.into())
1670 .collect();
1671
1672 let output = attribute_vmos(AttributionData {
1673 principals_vec: principals,
1674 resources_vec: resources,
1675 resource_names,
1676 attributions,
1677 })
1678 .summary();
1679
1680 assert_eq!(output.unclaimed, 0);
1681 assert_eq!(output.principals.len(), 2);
1682
1683 let principals: HashMap<u64, PrincipalSummary> =
1684 output.principals.into_iter().map(|p| (p.id, p)).collect();
1685
1686 assert_eq!(
1687 principals.get(&1).unwrap(),
1688 &PrincipalSummary {
1689 id: 1,
1690 name: "component_manager".to_owned(),
1691 principal_type: "R".to_owned(),
1692 committed_private: 0,
1693 committed_scaled: 0.0,
1694 committed_total: 0,
1695 populated_private: 0,
1696 populated_scaled: 0.0,
1697 populated_total: 0,
1698 attributor: None,
1699 processes: vec![],
1700 vmos: vec![].into_iter().collect(),
1701 }
1702 );
1703
1704 assert_eq!(
1705 principals.get(&2).unwrap(),
1706 &PrincipalSummary {
1707 id: 2,
1708 name: "component 2".to_owned(),
1709 principal_type: "R".to_owned(),
1710 committed_private: 1024,
1711 committed_scaled: 1024.0,
1712 committed_total: 1024,
1713 populated_private: 2048,
1714 populated_scaled: 2048.0,
1715 populated_total: 2048,
1716 attributor: Some("component_manager".to_owned()),
1717 processes: vec!["component_process (1002)".to_owned()],
1718 vmos: vec![
1719 (
1720 name::ZXName::from_string_lossy("component_vmo"),
1721 VmoSummary {
1722 count: 1,
1723 committed_private: 0,
1724 committed_scaled: 0.0,
1725 committed_total: 0,
1726 populated_private: 0,
1727 populated_scaled: 0.0,
1728 populated_total: 0,
1729 ..Default::default()
1730 }
1731 ),
1732 (
1733 name::ZXName::from_string_lossy("component_vmo_parent"),
1734 VmoSummary {
1735 count: 1,
1736 committed_private: 1024,
1737 committed_scaled: 1024.0,
1738 committed_total: 1024,
1739 populated_private: 2048,
1740 populated_scaled: 2048.0,
1741 populated_total: 2048,
1742 ..Default::default()
1743 }
1744 ),
1745 ]
1746 .into_iter()
1747 .collect(),
1748 }
1749 );
1750 }
1751
1752 fn make_principal_def(id: u64, name: &str) -> fplugin::Principal {
1753 fplugin::Principal {
1754 identifier: Some(fplugin::PrincipalIdentifier { id }),
1755 description: Some(fplugin::Description::Component(name.to_owned())),
1756 principal_type: Some(fplugin::PrincipalType::Runnable),
1757 parent: None,
1758 ..Default::default()
1759 }
1760 }
1761
1762 fn make_attribution_def(
1763 source: u64,
1764 subject: u64,
1765 resources: Vec<fplugin::ResourceReference>,
1766 ) -> fplugin::Attribution {
1767 fplugin::Attribution {
1768 source: Some(fplugin::PrincipalIdentifier { id: source }),
1769 subject: Some(fplugin::PrincipalIdentifier { id: subject }),
1770 resources: Some(resources),
1771 ..Default::default()
1772 }
1773 }
1774
1775 fn make_test_attribution_data(
1776 principals: Vec<fplugin::Principal>,
1777 resources: Vec<fplugin::Resource>,
1778 resource_names: Vec<name::ZXName>,
1779 attributions: Vec<fplugin::Attribution>,
1780 ) -> AttributionData {
1781 AttributionData {
1782 principals_vec: principals.into_iter().map(|p| p.into()).collect(),
1783 resources_vec: resources.into_iter().map(|r| r.into()).collect(),
1784 resource_names,
1785 attributions: attributions.into_iter().map(|a| a.into()).collect(),
1786 }
1787 }
1788
1789 #[test]
1798 fn test_process_claims_fast_path_and_dag() {
1799 let mut res = InflatedResource::new(
1800 fplugin::Resource {
1801 koid: Some(100),
1802 name_index: Some(0),
1803 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo::default())),
1804 ..Default::default()
1805 }
1806 .into(),
1807 );
1808 res.claims.insert(Claim {
1809 source: GlobalPrincipalIdentifier::new_for_test(1),
1810 subject: GlobalPrincipalIdentifier::new_for_test(2),
1811 claim_type: ClaimType::Direct,
1812 });
1813 res.claims.insert(Claim {
1814 source: GlobalPrincipalIdentifier::new_for_test(2),
1815 subject: GlobalPrincipalIdentifier::new_for_test(3),
1816 claim_type: ClaimType::Direct,
1817 });
1818 res.claims.insert(Claim {
1819 source: GlobalPrincipalIdentifier::new_for_test(2),
1820 subject: GlobalPrincipalIdentifier::new_for_test(4),
1821 claim_type: ClaimType::Direct,
1822 });
1823
1824 res.process_claims();
1825
1826 let mut final_subjects: Vec<u64> = res.claims.iter().map(|c| c.subject.0.get()).collect();
1827 final_subjects.sort_unstable();
1828 assert_eq!(final_subjects, vec![3, 4]);
1829 }
1830
1831 #[test]
1840 fn test_claim_propagation_hierarchy_and_overrides() {
1841 let resource_names = vec![
1842 name::ZXName::from_string_lossy("root_job"),
1843 name::ZXName::from_string_lossy("child_job"),
1844 name::ZXName::from_string_lossy("child_proc"),
1845 name::ZXName::from_string_lossy("child_vmo"),
1846 ];
1847 let principals = vec![
1848 make_principal_def(1, "parent_principal"),
1849 make_principal_def(2, "child_principal"),
1850 ];
1851 let resources = vec![
1852 fplugin::Resource {
1853 koid: Some(1000),
1854 name_index: Some(0),
1855 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1856 child_jobs: Some(vec![1001]),
1857 processes: Some(vec![]),
1858 ..Default::default()
1859 })),
1860 ..Default::default()
1861 },
1862 fplugin::Resource {
1863 koid: Some(1001),
1864 name_index: Some(1),
1865 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1866 child_jobs: Some(vec![]),
1867 processes: Some(vec![1002]),
1868 ..Default::default()
1869 })),
1870 ..Default::default()
1871 },
1872 fplugin::Resource {
1873 koid: Some(1002),
1874 name_index: Some(2),
1875 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1876 vmos: Some(vec![1003]),
1877 mappings: None,
1878 ..Default::default()
1879 })),
1880 ..Default::default()
1881 },
1882 fplugin::Resource {
1883 koid: Some(1003),
1884 name_index: Some(3),
1885 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1886 private_populated_bytes: Some(1000),
1887 scaled_populated_bytes: Some(1000),
1888 total_populated_bytes: Some(1000),
1889 ..Default::default()
1890 })),
1891 ..Default::default()
1892 },
1893 ];
1894 let attributions = vec![
1895 make_attribution_def(1, 1, vec![fplugin::ResourceReference::KernelObject(1000)]),
1896 make_attribution_def(2, 2, vec![fplugin::ResourceReference::KernelObject(1001)]),
1897 ];
1898
1899 let processed = attribute_vmos(make_test_attribution_data(
1900 principals,
1901 resources,
1902 resource_names,
1903 attributions,
1904 ));
1905
1906 let vmo_res = processed.resources.get(&1003).expect("VMO 1003 should exist");
1907 let vmo_subjects: HashSet<u64> = vmo_res.claims.iter().map(|c| c.subject.0.get()).collect();
1908 assert_eq!(vmo_subjects, HashSet::from([2]));
1909 }
1910
1911 #[test]
1923 fn test_vmo_ancestry_edge_cases() {
1924 let resource_names = vec![
1925 name::ZXName::from_string_lossy("grandparent"),
1926 name::ZXName::from_string_lossy("parent"),
1927 name::ZXName::from_string_lossy("child"),
1928 name::ZXName::from_string_lossy("self_ref"),
1929 ];
1930 let principals = vec![make_principal_def(1, "vmo_owner")];
1931 let resources = vec![
1932 fplugin::Resource {
1933 koid: Some(2001),
1934 name_index: Some(0),
1935 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1936 parent: None,
1937 total_populated_bytes: Some(4096),
1938 ..Default::default()
1939 })),
1940 ..Default::default()
1941 },
1942 fplugin::Resource {
1943 koid: Some(2002),
1944 name_index: Some(1),
1945 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1946 parent: Some(2001),
1947 total_populated_bytes: Some(4096),
1948 ..Default::default()
1949 })),
1950 ..Default::default()
1951 },
1952 fplugin::Resource {
1953 koid: Some(2003),
1954 name_index: Some(2),
1955 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1956 parent: Some(2002),
1957 total_populated_bytes: Some(0),
1958 ..Default::default()
1959 })),
1960 ..Default::default()
1961 },
1962 fplugin::Resource {
1963 koid: Some(2004),
1964 name_index: Some(3),
1965 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1966 parent: Some(2004),
1967 total_populated_bytes: Some(0),
1968 ..Default::default()
1969 })),
1970 ..Default::default()
1971 },
1972 ];
1973 let attributions = vec![
1974 make_attribution_def(1, 1, vec![fplugin::ResourceReference::KernelObject(2003)]),
1975 make_attribution_def(1, 1, vec![fplugin::ResourceReference::KernelObject(2004)]),
1976 ];
1977
1978 let processed = attribute_vmos(make_test_attribution_data(
1979 principals,
1980 resources,
1981 resource_names,
1982 attributions,
1983 ));
1984
1985 let parent_res = processed.resources.get(&2002).unwrap();
1986 assert!(parent_res.claims.iter().any(|c| c.claim_type == ClaimType::Child));
1987 let grandparent_res = processed.resources.get(&2001).unwrap();
1988 assert!(grandparent_res.claims.iter().any(|c| c.claim_type == ClaimType::Child));
1989
1990 let p1 = processed.principals.get(&GlobalPrincipalIdentifier::new_for_test(1)).unwrap();
1991 assert!(p1.resources.contains(&2001));
1992 assert!(p1.resources.contains(&2002));
1993 assert!(p1.resources.contains(&2003));
1994 assert!(p1.resources.contains(&2004));
1995 }
1996
1997 #[test]
2006 fn test_process_mapped_attributions() {
2007 let resource_names =
2008 vec![name::ZXName::from_string_lossy("proc1"), name::ZXName::from_string_lossy("vmo1")];
2009 let principals = vec![make_principal_def(1, "mapper_principal")];
2010 let resources = vec![
2011 fplugin::Resource {
2012 koid: Some(3001),
2013 name_index: Some(0),
2014 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
2015 vmos: Some(vec![3002]),
2016 mappings: Some(vec![fplugin::Mapping {
2017 vmo: Some(3002),
2018 address_base: Some(0x1000),
2019 size: Some(0x1000),
2020 ..Default::default()
2021 }]),
2022 ..Default::default()
2023 })),
2024 ..Default::default()
2025 },
2026 fplugin::Resource {
2027 koid: Some(3002),
2028 name_index: Some(1),
2029 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2030 total_populated_bytes: Some(4096),
2031 ..Default::default()
2032 })),
2033 ..Default::default()
2034 },
2035 ];
2036 let attributions = vec![make_attribution_def(
2037 1,
2038 1,
2039 vec![
2040 fplugin::ResourceReference::ProcessMapped(fplugin::ProcessMapped {
2041 process: 3001,
2042 base: 0x1000,
2043 len: 0x1000,
2044 hint_skip_handle_table: false,
2045 }),
2046 fplugin::ResourceReference::ProcessMapped(fplugin::ProcessMapped {
2047 process: 9999,
2048 base: 0x2000,
2049 len: 0x1000,
2050 hint_skip_handle_table: false,
2051 }),
2052 ],
2053 )];
2054
2055 let processed = attribute_vmos(make_test_attribution_data(
2056 principals,
2057 resources,
2058 resource_names,
2059 attributions,
2060 ));
2061
2062 let vmo_res = processed.resources.get(&3002).unwrap();
2063 let vmo_subjects: HashSet<u64> = vmo_res.claims.iter().map(|c| c.subject.0.get()).collect();
2064 assert_eq!(vmo_subjects, HashSet::from([1]));
2065 }
2066
2067 #[test]
2075 fn test_attribute_vmos_principal_resources_dedup() {
2076 let resource_names = vec![
2077 name::ZXName::from_string_lossy("job"),
2078 name::ZXName::from_string_lossy("proc"),
2079 name::ZXName::from_string_lossy("vmo"),
2080 ];
2081 let principals = vec![make_principal_def(1, "p1")];
2082 let resources = vec![
2083 fplugin::Resource {
2084 koid: Some(5001),
2085 name_index: Some(0),
2086 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
2087 child_jobs: Some(vec![]),
2088 processes: Some(vec![5002]),
2089 ..Default::default()
2090 })),
2091 ..Default::default()
2092 },
2093 fplugin::Resource {
2094 koid: Some(5002),
2095 name_index: Some(1),
2096 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
2097 vmos: Some(vec![5003]),
2098 mappings: None,
2099 ..Default::default()
2100 })),
2101 ..Default::default()
2102 },
2103 fplugin::Resource {
2104 koid: Some(5003),
2105 name_index: Some(2),
2106 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2107 total_populated_bytes: Some(4096),
2108 ..Default::default()
2109 })),
2110 ..Default::default()
2111 },
2112 ];
2113 let attributions = vec![make_attribution_def(
2114 1,
2115 1,
2116 vec![
2117 fplugin::ResourceReference::KernelObject(5001),
2118 fplugin::ResourceReference::KernelObject(5003),
2119 ],
2120 )];
2121
2122 let processed = attribute_vmos(make_test_attribution_data(
2123 principals,
2124 resources,
2125 resource_names,
2126 attributions,
2127 ));
2128
2129 let p1 = processed.principals.get(&GlobalPrincipalIdentifier::new_for_test(1)).unwrap();
2130 assert_eq!(p1.resources.len(), 2);
2131 assert!(p1.resources.contains(&5002));
2132 assert!(p1.resources.contains(&5003));
2133 }
2134
2135 #[test]
2145 fn test_vmo_ancestry_and_principal_resource_accumulation() {
2146 let resource_names = vec![
2147 name::ZXName::from_string_lossy("proc"),
2148 name::ZXName::from_string_lossy("root_vmo"),
2149 name::ZXName::from_string_lossy("mid_vmo"),
2150 name::ZXName::from_string_lossy("leaf_vmo1"),
2151 name::ZXName::from_string_lossy("leaf_vmo2"),
2152 ];
2153 let principals = vec![make_principal_def(1, "p1"), make_principal_def(2, "p2")];
2154 let resources = vec![
2155 fplugin::Resource {
2156 koid: Some(8000),
2157 name_index: Some(0),
2158 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
2159 vmos: Some(vec![8001, 8002, 8003, 8004]),
2160 mappings: None,
2161 ..Default::default()
2162 })),
2163 ..Default::default()
2164 },
2165 fplugin::Resource {
2166 koid: Some(8001),
2167 name_index: Some(1),
2168 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2169 parent: None,
2170 total_populated_bytes: Some(8192),
2171 ..Default::default()
2172 })),
2173 ..Default::default()
2174 },
2175 fplugin::Resource {
2176 koid: Some(8002),
2177 name_index: Some(2),
2178 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2179 parent: Some(8001),
2180 total_populated_bytes: Some(0),
2181 ..Default::default()
2182 })),
2183 ..Default::default()
2184 },
2185 fplugin::Resource {
2186 koid: Some(8003),
2187 name_index: Some(3),
2188 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2189 parent: Some(8002),
2190 total_populated_bytes: Some(0),
2191 ..Default::default()
2192 })),
2193 ..Default::default()
2194 },
2195 fplugin::Resource {
2196 koid: Some(8004),
2197 name_index: Some(4),
2198 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2199 parent: Some(8002),
2200 total_populated_bytes: Some(0),
2201 ..Default::default()
2202 })),
2203 ..Default::default()
2204 },
2205 ];
2206 let attributions = vec![
2207 make_attribution_def(
2208 1,
2209 1,
2210 vec![
2211 fplugin::ResourceReference::KernelObject(8000),
2212 fplugin::ResourceReference::KernelObject(8003),
2213 ],
2214 ),
2215 make_attribution_def(1, 2, vec![fplugin::ResourceReference::KernelObject(8004)]),
2216 ];
2217
2218 let processed = attribute_vmos(make_test_attribution_data(
2219 principals,
2220 resources,
2221 resource_names,
2222 attributions,
2223 ));
2224
2225 let p1 = processed.principals.get(&GlobalPrincipalIdentifier::new_for_test(1)).unwrap();
2226 assert_eq!(p1.resources.len(), 4);
2227 assert!(p1.resources.contains(&8000));
2228 assert!(p1.resources.contains(&8001));
2229 assert!(p1.resources.contains(&8002));
2230 assert!(p1.resources.contains(&8003));
2231
2232 let p2 = processed.principals.get(&GlobalPrincipalIdentifier::new_for_test(2)).unwrap();
2233 assert_eq!(p2.resources.len(), 3);
2234 assert!(p2.resources.contains(&8001));
2235 assert!(p2.resources.contains(&8002));
2236 assert!(p2.resources.contains(&8004));
2237
2238 let mid_vmo = processed.resources.get(&8002).unwrap();
2239 assert!(
2240 mid_vmo
2241 .claims
2242 .iter()
2243 .any(|c| c.claim_type == ClaimType::Child && c.subject.0.get() == 1)
2244 );
2245 assert!(
2246 mid_vmo
2247 .claims
2248 .iter()
2249 .any(|c| c.claim_type == ClaimType::Child && c.subject.0.get() == 2)
2250 );
2251
2252 let root_vmo = processed.resources.get(&8001).unwrap();
2253 assert!(
2254 root_vmo
2255 .claims
2256 .iter()
2257 .any(|c| c.claim_type == ClaimType::Child && c.subject.0.get() == 1)
2258 );
2259 assert!(
2260 root_vmo
2261 .claims
2262 .iter()
2263 .any(|c| c.claim_type == ClaimType::Child && c.subject.0.get() == 2)
2264 );
2265 }
2266
2267 #[test]
2283 fn test_process_mapped_binary_search_and_boundaries() {
2284 let resource_names = vec![
2285 name::ZXName::from_string_lossy("test_proc"),
2286 name::ZXName::from_string_lossy("vmo1"),
2287 name::ZXName::from_string_lossy("vmo2"),
2288 name::ZXName::from_string_lossy("vmo3"),
2289 name::ZXName::from_string_lossy("vmo4"),
2290 name::ZXName::from_string_lossy("vmo5"),
2291 name::ZXName::from_string_lossy("vmo6"),
2292 ];
2293 let principals = vec![make_principal_def(1, "vmar_principal")];
2294 let resources = vec![
2295 fplugin::Resource {
2296 koid: Some(6000),
2297 name_index: Some(0),
2298 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
2299 vmos: Some(vec![6001, 6002, 6003, 6004, 6005, 6006]),
2300 mappings: Some(vec![
2301 fplugin::Mapping {
2302 vmo: Some(6001),
2303 address_base: Some(0x1000),
2304 size: Some(0x1000),
2305 ..Default::default()
2306 },
2307 fplugin::Mapping {
2308 vmo: Some(6002),
2309 address_base: Some(0x3000),
2310 size: Some(0x1000),
2311 ..Default::default()
2312 },
2313 fplugin::Mapping {
2314 vmo: Some(6003),
2315 address_base: Some(0x4000),
2316 size: Some(0x2000),
2317 ..Default::default()
2318 },
2319 fplugin::Mapping {
2320 vmo: Some(6004),
2321 address_base: Some(0x6000),
2322 size: Some(0x2000),
2323 ..Default::default()
2324 },
2325 fplugin::Mapping {
2326 vmo: Some(6005),
2327 address_base: Some(0x7000),
2328 size: Some(0x1000),
2329 ..Default::default()
2330 },
2331 fplugin::Mapping {
2332 vmo: Some(6006),
2333 address_base: Some(0xa000),
2334 size: Some(0x1000),
2335 ..Default::default()
2336 },
2337 ]),
2338 ..Default::default()
2339 })),
2340 ..Default::default()
2341 },
2342 fplugin::Resource {
2343 koid: Some(6001),
2344 name_index: Some(1),
2345 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2346 private_committed_bytes: Some(4096),
2347 private_populated_bytes: Some(4096),
2348 scaled_committed_bytes: Some(4096),
2349 scaled_populated_bytes: Some(4096),
2350 total_committed_bytes: Some(4096),
2351 total_populated_bytes: Some(4096),
2352 ..Default::default()
2353 })),
2354 ..Default::default()
2355 },
2356 fplugin::Resource {
2357 koid: Some(6002),
2358 name_index: Some(2),
2359 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2360 private_committed_bytes: Some(4096),
2361 private_populated_bytes: Some(4096),
2362 scaled_committed_bytes: Some(4096),
2363 scaled_populated_bytes: Some(4096),
2364 total_committed_bytes: Some(4096),
2365 total_populated_bytes: Some(4096),
2366 ..Default::default()
2367 })),
2368 ..Default::default()
2369 },
2370 fplugin::Resource {
2371 koid: Some(6003),
2372 name_index: Some(3),
2373 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2374 private_committed_bytes: Some(8192),
2375 private_populated_bytes: Some(8192),
2376 scaled_committed_bytes: Some(8192),
2377 scaled_populated_bytes: Some(8192),
2378 total_committed_bytes: Some(8192),
2379 total_populated_bytes: Some(8192),
2380 ..Default::default()
2381 })),
2382 ..Default::default()
2383 },
2384 fplugin::Resource {
2385 koid: Some(6004),
2386 name_index: Some(4),
2387 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2388 private_committed_bytes: Some(8192),
2389 private_populated_bytes: Some(8192),
2390 scaled_committed_bytes: Some(8192),
2391 scaled_populated_bytes: Some(8192),
2392 total_committed_bytes: Some(8192),
2393 total_populated_bytes: Some(8192),
2394 ..Default::default()
2395 })),
2396 ..Default::default()
2397 },
2398 fplugin::Resource {
2399 koid: Some(6005),
2400 name_index: Some(5),
2401 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2402 private_committed_bytes: Some(4096),
2403 private_populated_bytes: Some(4096),
2404 scaled_committed_bytes: Some(4096),
2405 scaled_populated_bytes: Some(4096),
2406 total_committed_bytes: Some(4096),
2407 total_populated_bytes: Some(4096),
2408 ..Default::default()
2409 })),
2410 ..Default::default()
2411 },
2412 fplugin::Resource {
2413 koid: Some(6006),
2414 name_index: Some(6),
2415 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
2416 private_committed_bytes: Some(4096),
2417 private_populated_bytes: Some(4096),
2418 scaled_committed_bytes: Some(4096),
2419 scaled_populated_bytes: Some(4096),
2420 total_committed_bytes: Some(4096),
2421 total_populated_bytes: Some(4096),
2422 ..Default::default()
2423 })),
2424 ..Default::default()
2425 },
2426 ];
2427 let attributions = vec![make_attribution_def(
2428 1,
2429 1,
2430 vec![fplugin::ResourceReference::ProcessMapped(fplugin::ProcessMapped {
2431 process: 6000,
2432 base: 0x3000,
2433 len: 0x4000,
2434 hint_skip_handle_table: false,
2435 })],
2436 )];
2437
2438 let processed = attribute_vmos(make_test_attribution_data(
2439 principals,
2440 resources,
2441 resource_names,
2442 attributions,
2443 ));
2444
2445 assert_eq!(
2447 processed
2448 .resources
2449 .get(&6002)
2450 .unwrap()
2451 .claims
2452 .iter()
2453 .map(|c| c.subject.0.get())
2454 .collect::<Vec<_>>(),
2455 vec![1]
2456 );
2457 assert_eq!(
2458 processed
2459 .resources
2460 .get(&6003)
2461 .unwrap()
2462 .claims
2463 .iter()
2464 .map(|c| c.subject.0.get())
2465 .collect::<Vec<_>>(),
2466 vec![1]
2467 );
2468 assert!(processed.resources.get(&6001).unwrap().claims.is_empty());
2470 assert!(processed.resources.get(&6004).unwrap().claims.is_empty());
2471 assert!(processed.resources.get(&6005).unwrap().claims.is_empty());
2472 assert!(processed.resources.get(&6006).unwrap().claims.is_empty());
2473
2474 let summary = processed.summary();
2475 assert_eq!(summary.principals.len(), 1);
2476 assert_eq!(summary.principals[0].processes, vec!["test_proc (6000)".to_owned()]);
2477 }
2478}