1use core::cell::RefCell;
6use core::convert::Into;
7use fidl_fuchsia_memory_attribution_plugin as fplugin;
8use futures::future::BoxFuture;
9use std::collections::{HashMap, HashSet};
10use summary::MemorySummary;
11
12pub mod digest;
13pub mod kernel_statistics;
14mod name;
15pub mod summary;
16pub use name::ZXName;
17
18#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
19pub struct PrincipalIdentifier(pub u64);
20
21impl From<fplugin::PrincipalIdentifier> for PrincipalIdentifier {
22 fn from(value: fplugin::PrincipalIdentifier) -> Self {
23 PrincipalIdentifier(value.id)
24 }
25}
26
27impl Into<fplugin::PrincipalIdentifier> for PrincipalIdentifier {
28 fn into(self) -> fplugin::PrincipalIdentifier {
29 fplugin::PrincipalIdentifier { id: self.0 }
30 }
31}
32
33#[derive(PartialEq, Eq, Clone, Debug, Hash)]
35pub enum PrincipalDescription {
36 Component(String),
37 Part(String),
38}
39
40impl From<fplugin::Description> for PrincipalDescription {
41 fn from(value: fplugin::Description) -> Self {
42 match value {
43 fplugin::Description::Component(s) => PrincipalDescription::Component(s),
44 fplugin::Description::Part(s) => PrincipalDescription::Part(s),
45 _ => unreachable!(),
46 }
47 }
48}
49
50impl Into<fplugin::Description> for PrincipalDescription {
51 fn into(self) -> fplugin::Description {
52 match self {
53 PrincipalDescription::Component(s) => fplugin::Description::Component(s),
54 PrincipalDescription::Part(s) => fplugin::Description::Part(s),
55 }
56 }
57}
58
59#[derive(PartialEq, Eq, Clone, Debug, Hash)]
61pub enum PrincipalType {
62 Runnable,
63 Part,
64}
65
66impl From<fplugin::PrincipalType> for PrincipalType {
67 fn from(value: fplugin::PrincipalType) -> Self {
68 match value {
69 fplugin::PrincipalType::Runnable => PrincipalType::Runnable,
70 fplugin::PrincipalType::Part => PrincipalType::Part,
71 _ => unreachable!(),
72 }
73 }
74}
75
76impl Into<fplugin::PrincipalType> for PrincipalType {
77 fn into(self) -> fplugin::PrincipalType {
78 match self {
79 PrincipalType::Runnable => fplugin::PrincipalType::Runnable,
80 PrincipalType::Part => fplugin::PrincipalType::Part,
81 }
82 }
83}
84
85#[derive(Clone, Debug, Eq, Hash, PartialEq)]
86pub struct Principal {
88 pub identifier: PrincipalIdentifier,
90 pub description: PrincipalDescription,
91 pub principal_type: PrincipalType,
92
93 pub parent: Option<PrincipalIdentifier>,
97}
98
99impl From<fplugin::Principal> for Principal {
102 fn from(value: fplugin::Principal) -> Self {
103 Principal {
104 identifier: value.identifier.unwrap().into(),
105 description: value.description.unwrap().into(),
106 principal_type: value.principal_type.unwrap().into(),
107 parent: value.parent.map(Into::into),
108 }
109 }
110}
111
112impl Into<fplugin::Principal> for Principal {
113 fn into(self) -> fplugin::Principal {
114 fplugin::Principal {
115 identifier: Some(self.identifier.into()),
116 description: Some(self.description.into()),
117 principal_type: Some(self.principal_type.into()),
118 parent: self.parent.map(Into::into),
119 ..Default::default()
120 }
121 }
122}
123
124pub struct InflatedPrincipal {
126 principal: Principal,
128
129 attribution_claims: HashMap<PrincipalIdentifier, Attribution>,
133 resources: HashSet<u64>,
136}
137
138impl InflatedPrincipal {
139 fn new(principal: Principal) -> InflatedPrincipal {
140 InflatedPrincipal {
141 principal,
142 attribution_claims: Default::default(),
143 resources: Default::default(),
144 }
145 }
146}
147
148impl InflatedPrincipal {
149 fn name(&self) -> &str {
150 match &self.principal.description {
151 PrincipalDescription::Component(component_name) => component_name,
152 PrincipalDescription::Part(part_name) => part_name,
153 }
154 }
155}
156
157#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
159pub enum ClaimType {
160 Direct,
162 Indirect,
165 Child,
167}
168
169#[derive(Clone, Copy, PartialEq, Eq, Hash)]
170pub struct Koid(u64);
171
172impl From<u64> for Koid {
173 fn from(value: u64) -> Self {
174 Koid(value)
175 }
176}
177
178#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
184pub struct Claim {
185 subject: PrincipalIdentifier,
187 source: PrincipalIdentifier,
189 claim_type: ClaimType,
190}
191
192#[derive(Clone, Debug, PartialEq)]
193pub struct Resource {
194 pub koid: u64,
195 pub name_index: usize,
196 pub resource_type: fplugin::ResourceType,
197}
198
199impl From<fplugin::Resource> for Resource {
200 fn from(value: fplugin::Resource) -> Self {
201 Resource {
202 koid: value.koid.unwrap(),
203 name_index: value.name_index.unwrap() as usize,
204 resource_type: value.resource_type.unwrap(),
205 }
206 }
207}
208
209impl Into<fplugin::Resource> for Resource {
210 fn into(self) -> fplugin::Resource {
211 fplugin::Resource {
212 koid: Some(self.koid),
213 name_index: Some(self.name_index as u64),
214 resource_type: Some(self.resource_type),
215 ..Default::default()
216 }
217 }
218}
219
220pub struct TaggedClaim(Claim, bool);
222
223#[derive(Debug)]
224pub struct InflatedResource {
225 resource: Resource,
226 claims: HashSet<Claim>,
227}
228
229impl InflatedResource {
230 fn new(resource: Resource) -> InflatedResource {
231 InflatedResource { resource, claims: Default::default() }
232 }
233
234 fn children(&self) -> Vec<u64> {
235 match &self.resource.resource_type {
236 fplugin::ResourceType::Job(job) => {
237 let mut r: Vec<u64> = job.child_jobs.iter().flatten().map(|k| *k).collect();
238 r.extend(job.processes.iter().flatten().map(|k| *k));
239 r
240 }
241 fplugin::ResourceType::Process(process) => {
242 process.vmos.iter().flatten().map(|k| *k).collect()
243 }
244 fplugin::ResourceType::Vmo(_) => Vec::new(),
245 _ => todo!(),
246 }
247 }
248
249 fn process_claims(&mut self) {
259 let mut claims_by_source: HashMap<PrincipalIdentifier, RefCell<Vec<TaggedClaim>>> =
260 Default::default();
261 let mut self_claims = Vec::new();
262
263 for claim in self.claims.iter().cloned() {
264 if claim.source == claim.subject {
265 self_claims.push(claim);
268 } else {
269 claims_by_source
270 .entry(claim.source)
271 .or_default()
272 .borrow_mut()
273 .push(TaggedClaim(claim, false));
274 }
275 }
276
277 self.claims = self_claims.into_iter().collect();
278 for (_, claimlist_refcell) in claims_by_source.iter() {
279 let mut claimlist = claimlist_refcell.borrow_mut();
280 for tagged_claim in claimlist.iter_mut() {
281 self.claims.extend(
282 InflatedResource::process_claims_recursive(tagged_claim, &claims_by_source)
283 .into_iter(),
284 );
285 }
286 }
287 }
288
289 fn process_claims_recursive(
291 tagged_claim: &mut TaggedClaim,
292 claims: &HashMap<PrincipalIdentifier, RefCell<Vec<TaggedClaim>>>,
293 ) -> Vec<Claim> {
294 let claim = match tagged_claim.1 {
295 true => {
296 return vec![];
298 }
299 false => {
300 tagged_claim.1 = true;
302 tagged_claim.0
303 }
304 };
305 let subject = &claim.subject;
306 let mut subject_claims = match claims.get(subject) {
308 Some(value_ref) => {
309 value_ref.try_borrow_mut().expect("Claims form a cycle, this is not supported")
314 }
315 None => {
316 return vec![claim];
318 }
319 };
320 let mut leaves = vec![];
321 for subject_claim in subject_claims.iter_mut() {
322 leaves.append(&mut InflatedResource::process_claims_recursive(subject_claim, claims));
323 }
324 leaves
325 }
326}
327
328#[derive(Clone)]
329pub struct Attribution {
331 pub source: PrincipalIdentifier,
333 pub subject: PrincipalIdentifier,
335 pub resources: Vec<ResourceReference>,
337}
338
339impl From<fplugin::Attribution> for Attribution {
340 fn from(value: fplugin::Attribution) -> Attribution {
341 Attribution {
342 source: value.source.unwrap().into(),
343 subject: value.subject.unwrap().into(),
344 resources: value.resources.unwrap().into_iter().map(|r| r.into()).collect(),
345 }
346 }
347}
348
349impl Into<fplugin::Attribution> for Attribution {
350 fn into(self) -> fplugin::Attribution {
351 fplugin::Attribution {
352 source: Some(self.source.into()),
353 subject: Some(self.subject.into()),
354 resources: Some(self.resources.into_iter().map(|r| r.into()).collect()),
355 ..Default::default()
356 }
357 }
358}
359
360#[derive(Clone, Copy)]
361pub enum ResourceReference {
364 KernelObject(u64),
369
370 ProcessMapped {
372 process: u64,
374
375 base: u64,
377
378 len: u64,
380 },
381}
382
383impl From<fplugin::ResourceReference> for ResourceReference {
384 fn from(value: fplugin::ResourceReference) -> ResourceReference {
385 match value {
386 fidl_fuchsia_memory_attribution_plugin::ResourceReference::KernelObject(ko) => {
387 ResourceReference::KernelObject(ko)
388 }
389 fidl_fuchsia_memory_attribution_plugin::ResourceReference::ProcessMapped(
390 fplugin::ProcessMapped { process, base, len },
391 ) => ResourceReference::ProcessMapped { process, base, len },
392 _ => unimplemented!(),
393 }
394 }
395}
396
397impl Into<fplugin::ResourceReference> for ResourceReference {
398 fn into(self) -> fplugin::ResourceReference {
399 match self {
400 ResourceReference::KernelObject(ko) => {
401 fidl_fuchsia_memory_attribution_plugin::ResourceReference::KernelObject(ko)
402 }
403 ResourceReference::ProcessMapped { process, base, len } => {
404 fidl_fuchsia_memory_attribution_plugin::ResourceReference::ProcessMapped(
405 fplugin::ProcessMapped { process, base, len },
406 )
407 }
408 }
409 }
410}
411
412pub struct AttributionData {
415 pub principals_vec: Vec<Principal>,
416 pub resources_vec: Vec<Resource>,
417 pub resource_names: Vec<ZXName>,
418 pub attributions: Vec<Attribution>,
419}
420
421pub trait ResourcesVisitor {
425 fn on_job(
426 &mut self,
427 job_koid: zx_types::zx_koid_t,
428 job_name: &ZXName,
429 job: fplugin::Job,
430 ) -> Result<(), zx_status::Status>;
431 fn on_process(
432 &mut self,
433 process_koid: zx_types::zx_koid_t,
434 process_name: &ZXName,
435 process: fplugin::Process,
436 ) -> Result<(), zx_status::Status>;
437 fn on_vmo(
438 &mut self,
439 vmo_koid: zx_types::zx_koid_t,
440 vmo_name: &ZXName,
441 vmo: fplugin::Vmo,
442 ) -> Result<(), zx_status::Status>;
443}
444
445pub trait AttributionDataProvider: Send + Sync + 'static {
446 fn get_attribution_data(&self) -> BoxFuture<'_, Result<AttributionData, anyhow::Error>>;
448 fn for_each_resource(&self, visitor: &mut impl ResourcesVisitor) -> Result<(), anyhow::Error>;
450}
451
452pub struct ProcessedAttributionData {
455 principals: HashMap<PrincipalIdentifier, RefCell<InflatedPrincipal>>,
456 resources: HashMap<u64, RefCell<InflatedResource>>,
457 resource_names: Vec<ZXName>,
458}
459
460impl ProcessedAttributionData {
461 fn new(
462 principals: HashMap<PrincipalIdentifier, RefCell<InflatedPrincipal>>,
463 resources: HashMap<u64, RefCell<InflatedResource>>,
464 resource_names: Vec<ZXName>,
465 ) -> Self {
466 Self { principals, resources, resource_names }
467 }
468
469 pub fn summary(&self) -> MemorySummary {
471 MemorySummary::build(&self.principals, &self.resources, &self.resource_names)
472 }
473}
474
475pub fn attribute_vmos(attribution_data: AttributionData) -> ProcessedAttributionData {
477 let principals: HashMap<PrincipalIdentifier, RefCell<InflatedPrincipal>> = attribution_data
479 .principals_vec
480 .into_iter()
481 .map(|p| (p.identifier.clone(), RefCell::new(InflatedPrincipal::new(p))))
482 .collect();
483
484 let mut resources: HashMap<u64, RefCell<InflatedResource>> = attribution_data
486 .resources_vec
487 .into_iter()
488 .map(|r| (r.koid, RefCell::new(InflatedResource::new(r))))
489 .collect();
490
491 for attribution in attribution_data.attributions {
493 principals.get(&attribution.subject.clone().into()).map(|p| {
494 p.borrow_mut().attribution_claims.insert(attribution.source.into(), attribution.clone())
495 });
496 for resource in attribution.resources {
497 match resource {
498 ResourceReference::KernelObject(koid) => {
499 if !resources.contains_key(&koid) {
500 continue;
501 }
502 resources.get_mut(&koid).unwrap().get_mut().claims.insert(Claim {
503 source: attribution.source.into(),
504 subject: attribution.subject.into(),
505 claim_type: ClaimType::Direct,
506 });
507 }
508 ResourceReference::ProcessMapped { process, base, len } => {
509 if !resources.contains_key(&process) {
510 continue;
511 }
512 let mut matched_vmos = Vec::new();
513 if let fplugin::ResourceType::Process(process_data) =
514 &resources.get(&process).unwrap().borrow().resource.resource_type
515 {
516 for mapping in process_data.mappings.iter().flatten() {
517 if mapping.address_base.unwrap() >= base
520 && mapping.address_base.unwrap() + mapping.size.unwrap()
521 <= base + len
522 {
523 matched_vmos.push(mapping.vmo.unwrap());
524 }
525 }
526 }
527 for vmo_koid in matched_vmos {
528 match resources.get_mut(&vmo_koid) {
529 Some(resource) => {
530 resource.get_mut().claims.insert(Claim {
531 source: attribution.source.into(),
532 subject: attribution.subject.into(),
533 claim_type: ClaimType::Direct,
534 });
535 }
536 None => {
537 }
541 }
542 }
543 }
544 }
545 }
546 }
547
548 for (_, resource_refcell) in &resources {
553 let resource = resource_refcell.borrow_mut();
554 let direct_claims: Vec<&Claim> = resource
556 .claims
557 .iter()
558 .filter(|claim| match claim.claim_type {
559 ClaimType::Direct => true,
560 _ => false,
561 })
562 .collect();
563
564 if direct_claims.is_empty() {
565 continue;
567 }
568
569 let propagated_claims: Vec<Claim> = direct_claims
570 .into_iter()
571 .map(|claim| Claim {
572 source: claim.source,
573 subject: claim.subject,
574 claim_type: ClaimType::Indirect,
575 })
576 .collect();
577 let mut frontier = Vec::new();
578 frontier.extend(resource.children());
579 while !frontier.is_empty() {
580 let child = frontier.pop().unwrap();
581 let mut child_resource = match resources.get(&child) {
582 Some(resource) => resource.borrow_mut(),
583 None => {
584 continue;
588 }
589 };
590 if child_resource.claims.iter().any(|c| c.claim_type == ClaimType::Direct) {
591 continue;
593 }
594 child_resource.claims.extend(propagated_claims.clone().iter());
595 frontier.extend(child_resource.children().iter());
596 }
597 }
598
599 for (_, resource_refcell) in &resources {
600 let mut resource = resource_refcell.borrow_mut();
601 resource.process_claims();
602 }
603
604 for (resource_id, resource_refcell) in &resources {
607 let resource = resource_refcell.borrow();
608 if let fplugin::ResourceType::Vmo(vmo) = &resource.resource.resource_type {
609 let mut ancestors = vec![*resource_id];
610 if vmo.total_populated_bytes.unwrap_or_default() == 0 {
617 let mut current_parent = vmo.parent;
618 while let Some(parent_koid) = current_parent {
622 if parent_koid == 0 {
623 panic!("Parent is not None but 0.");
624 }
625 ancestors.push(parent_koid);
626 let mut current_resource = match resources.get(&parent_koid) {
627 Some(res) => res.borrow_mut(),
628 None => break,
629 };
630 current_resource.claims.extend(resource.claims.iter().map(|c| Claim {
631 subject: c.subject,
632 source: c.source,
633 claim_type: ClaimType::Child,
634 }));
635 current_parent = match ¤t_resource.resource.resource_type {
636 fplugin::ResourceType::Job(_) => panic!("This should not happen"),
637 fplugin::ResourceType::Process(_) => panic!("This should not happen"),
638 fplugin::ResourceType::Vmo(current_vmo) => current_vmo.parent,
639 _ => unimplemented!(),
640 };
641 }
642 }
643
644 for claim in &resource.claims {
645 principals
646 .get(&claim.subject)
647 .unwrap()
648 .borrow_mut()
649 .resources
650 .extend(ancestors.iter());
651 }
652 } else if let fplugin::ResourceType::Process(_) = &resource.resource.resource_type {
653 for claim in &resource.claims {
654 principals
655 .get(&claim.subject)
656 .unwrap()
657 .borrow_mut()
658 .resources
659 .insert(resource.resource.koid);
660 }
661 }
662 }
663
664 ProcessedAttributionData::new(principals, resources, attribution_data.resource_names)
665}
666
667pub mod testing {
668 use crate::{AttributionData, AttributionDataProvider, Resource, ResourcesVisitor};
669 use fidl_fuchsia_memory_attribution_plugin::ResourceType;
670 use futures::future::{ready, BoxFuture};
671
672 pub struct FakeAttributionDataProvider {
673 pub attribution_data: AttributionData,
674 }
675
676 impl AttributionDataProvider for FakeAttributionDataProvider {
677 fn get_attribution_data(&self) -> BoxFuture<'_, Result<AttributionData, anyhow::Error>> {
678 Box::pin(ready(Ok(AttributionData {
679 principals_vec: self.attribution_data.principals_vec.clone(),
680 resources_vec: self.attribution_data.resources_vec.clone(),
681 resource_names: self.attribution_data.resource_names.clone(),
682 attributions: self.attribution_data.attributions.clone(),
683 })))
684 }
685
686 fn for_each_resource(
687 &self,
688 visitor: &mut impl ResourcesVisitor,
689 ) -> Result<(), anyhow::Error> {
690 for resource in &self.attribution_data.resources_vec {
691 if let Resource {
692 koid, name_index, resource_type: ResourceType::Vmo(vmo), ..
693 } = resource
694 {
695 visitor.on_vmo(
696 *koid,
697 &self.attribution_data.resource_names[*name_index],
698 vmo.clone(),
699 )?;
700 }
701 }
702 for resource in &self.attribution_data.resources_vec {
703 if let Resource {
704 koid,
705 name_index,
706 resource_type: ResourceType::Process(process),
707 ..
708 } = resource
709 {
710 visitor.on_process(
711 *koid,
712 &self.attribution_data.resource_names[*name_index],
713 process.clone(),
714 )?;
715 }
716 }
717 for resource in &self.attribution_data.resources_vec {
718 if let Resource {
719 koid, name_index, resource_type: ResourceType::Job(job), ..
720 } = resource
721 {
722 visitor.on_job(
723 *koid,
724 &self.attribution_data.resource_names[*name_index],
725 job.clone(),
726 )?;
727 }
728 }
729 Ok(())
730 }
731 }
732}
733
734#[cfg(test)]
735mod tests {
736 use super::*;
737 use std::collections::HashMap;
738 use summary::{PrincipalSummary, VmoSummary};
739
740 #[test]
741 fn test_gather_resources() {
742 let resource_names = vec![
766 ZXName::from_string_lossy("root_job"),
767 ZXName::from_string_lossy("root_process"),
768 ZXName::from_string_lossy("root_vmo"),
769 ZXName::from_string_lossy("shared_vmo"),
770 ZXName::from_string_lossy("runner_job"),
771 ZXName::from_string_lossy("runner_process"),
772 ZXName::from_string_lossy("runner_vmo"),
773 ZXName::from_string_lossy("component_vmo"),
774 ZXName::from_string_lossy("component_2_job"),
775 ZXName::from_string_lossy("2_process"),
776 ZXName::from_string_lossy("2_vmo"),
777 ZXName::from_string_lossy("2_vmo_parent"),
778 ZXName::from_string_lossy("component_vmo_mapped"),
779 ZXName::from_string_lossy("component_vmo_mapped2"),
780 ];
781
782 let attributions = vec![
783 fplugin::Attribution {
784 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
785 subject: Some(fplugin::PrincipalIdentifier { id: 0 }),
786 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
787 ..Default::default()
788 },
789 fplugin::Attribution {
790 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
791 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
792 resources: Some(vec![fplugin::ResourceReference::KernelObject(1004)]),
793 ..Default::default()
794 },
795 fplugin::Attribution {
796 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
797 subject: Some(fplugin::PrincipalIdentifier { id: 2 }),
798 resources: Some(vec![fplugin::ResourceReference::KernelObject(1008)]),
799 ..Default::default()
800 },
801 fplugin::Attribution {
802 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
803 subject: Some(fplugin::PrincipalIdentifier { id: 3 }),
804 resources: Some(vec![
805 fplugin::ResourceReference::KernelObject(1007),
806 fplugin::ResourceReference::ProcessMapped(fplugin::ProcessMapped {
807 process: 1005,
808 base: 1024,
809 len: 1024,
810 }),
811 ]),
812 ..Default::default()
813 },
814 ]
815 .into_iter()
816 .map(|a| a.into())
817 .collect();
818
819 let principals = vec![
820 fplugin::Principal {
821 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
822 description: Some(fplugin::Description::Component("root".to_owned())),
823 principal_type: Some(fplugin::PrincipalType::Runnable),
824 parent: None,
825 ..Default::default()
826 },
827 fplugin::Principal {
828 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
829 description: Some(fplugin::Description::Component("runner".to_owned())),
830 principal_type: Some(fplugin::PrincipalType::Runnable),
831 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
832 ..Default::default()
833 },
834 fplugin::Principal {
835 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
836 description: Some(fplugin::Description::Component("component 2".to_owned())),
837 principal_type: Some(fplugin::PrincipalType::Runnable),
838 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
839 ..Default::default()
840 },
841 fplugin::Principal {
842 identifier: Some(fplugin::PrincipalIdentifier { id: 3 }),
843 description: Some(fplugin::Description::Component("component 3".to_owned())),
844 principal_type: Some(fplugin::PrincipalType::Runnable),
845 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
846 ..Default::default()
847 },
848 ]
849 .into_iter()
850 .map(|p| p.into())
851 .collect();
852
853 let resources = vec![
854 fplugin::Resource {
855 koid: Some(1000),
856 name_index: Some(0),
857 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
858 child_jobs: Some(vec![1004, 1008]),
859 processes: Some(vec![1001]),
860 ..Default::default()
861 })),
862 ..Default::default()
863 },
864 fplugin::Resource {
865 koid: Some(1001),
866 name_index: Some(1),
867 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
868 vmos: Some(vec![1002, 1003]),
869 mappings: None,
870 ..Default::default()
871 })),
872 ..Default::default()
873 },
874 fplugin::Resource {
875 koid: Some(1002),
876 name_index: Some(2),
877 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
878 private_committed_bytes: Some(1024),
879 private_populated_bytes: Some(2048),
880 scaled_committed_bytes: Some(1024),
881 scaled_populated_bytes: Some(2048),
882 total_committed_bytes: Some(1024),
883 total_populated_bytes: Some(2048),
884 ..Default::default()
885 })),
886 ..Default::default()
887 },
888 fplugin::Resource {
889 koid: Some(1003),
890 name_index: Some(3),
891 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
892 private_committed_bytes: Some(1024),
893 private_populated_bytes: Some(2048),
894 scaled_committed_bytes: Some(1024),
895 scaled_populated_bytes: Some(2048),
896 total_committed_bytes: Some(1024),
897 total_populated_bytes: Some(2048),
898 ..Default::default()
899 })),
900 ..Default::default()
901 },
902 fplugin::Resource {
903 koid: Some(1004),
904 name_index: Some(4),
905 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
906 child_jobs: Some(vec![]),
907 processes: Some(vec![1005]),
908 ..Default::default()
909 })),
910 ..Default::default()
911 },
912 fplugin::Resource {
913 koid: Some(1005),
914 name_index: Some(5),
915 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
916 vmos: Some(vec![1006, 1007, 1012]),
917 mappings: Some(vec![
918 fplugin::Mapping {
919 vmo: Some(1006),
920 address_base: Some(0),
921 size: Some(512),
922 ..Default::default()
923 },
924 fplugin::Mapping {
925 vmo: Some(1012),
926 address_base: Some(1024),
927 size: Some(512),
928 ..Default::default()
929 },
930 fplugin::Mapping {
931 vmo: Some(1013),
932 address_base: Some(1536),
933 size: Some(512),
934 ..Default::default()
935 },
936 fplugin::Mapping {
937 vmo: Some(1006),
938 address_base: Some(2048),
939 size: Some(512),
940 ..Default::default()
941 },
942 ]),
943 ..Default::default()
944 })),
945 ..Default::default()
946 },
947 fplugin::Resource {
948 koid: Some(1006),
949 name_index: Some(6),
950 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
951 private_committed_bytes: Some(1024),
952 private_populated_bytes: Some(2048),
953 scaled_committed_bytes: Some(1024),
954 scaled_populated_bytes: Some(2048),
955 total_committed_bytes: Some(1024),
956 total_populated_bytes: Some(2048),
957 ..Default::default()
958 })),
959 ..Default::default()
960 },
961 fplugin::Resource {
962 koid: Some(1007),
963 name_index: Some(7),
964 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
965 private_committed_bytes: Some(128),
966 private_populated_bytes: Some(256),
967 scaled_committed_bytes: Some(128),
968 scaled_populated_bytes: Some(256),
969 total_committed_bytes: Some(128),
970 total_populated_bytes: Some(256),
971 ..Default::default()
972 })),
973 ..Default::default()
974 },
975 fplugin::Resource {
976 koid: Some(1008),
977 name_index: Some(8),
978 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
979 child_jobs: Some(vec![]),
980 processes: Some(vec![1009]),
981 ..Default::default()
982 })),
983 ..Default::default()
984 },
985 fplugin::Resource {
986 koid: Some(1009),
987 name_index: Some(9),
988 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
989 vmos: Some(vec![1010, 1003]),
990 mappings: None,
991 ..Default::default()
992 })),
993 ..Default::default()
994 },
995 fplugin::Resource {
996 koid: Some(1010),
997 name_index: Some(10),
998 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
999 parent: Some(1011),
1000 private_committed_bytes: Some(1024),
1001 private_populated_bytes: Some(2048),
1002 scaled_committed_bytes: Some(1024),
1003 scaled_populated_bytes: Some(2048),
1004 total_committed_bytes: Some(1024),
1005 total_populated_bytes: Some(2048),
1006 ..Default::default()
1007 })),
1008 ..Default::default()
1009 },
1010 fplugin::Resource {
1011 koid: Some(1011),
1012 name_index: Some(11),
1013 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1014 private_committed_bytes: Some(1024),
1015 private_populated_bytes: Some(2048),
1016 scaled_committed_bytes: Some(1024),
1017 scaled_populated_bytes: Some(2048),
1018 total_committed_bytes: Some(1024),
1019 total_populated_bytes: Some(2048),
1020 ..Default::default()
1021 })),
1022 ..Default::default()
1023 },
1024 fplugin::Resource {
1025 koid: Some(1012),
1026 name_index: Some(12),
1027 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1028 private_committed_bytes: Some(1024),
1029 private_populated_bytes: Some(2048),
1030 scaled_committed_bytes: Some(1024),
1031 scaled_populated_bytes: Some(2048),
1032 total_committed_bytes: Some(1024),
1033 total_populated_bytes: Some(2048),
1034 ..Default::default()
1035 })),
1036 ..Default::default()
1037 },
1038 fplugin::Resource {
1039 koid: Some(1013),
1040 name_index: Some(13),
1041 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1042 private_committed_bytes: Some(1024),
1043 private_populated_bytes: Some(2048),
1044 scaled_committed_bytes: Some(1024),
1045 scaled_populated_bytes: Some(2048),
1046 total_committed_bytes: Some(1024),
1047 total_populated_bytes: Some(2048),
1048 ..Default::default()
1049 })),
1050 ..Default::default()
1051 },
1052 ]
1053 .into_iter()
1054 .map(|r| r.into())
1055 .collect();
1056
1057 let output = attribute_vmos(AttributionData {
1058 principals_vec: principals,
1059 resources_vec: resources,
1060 resource_names,
1061 attributions,
1062 })
1063 .summary();
1064
1065 assert_eq!(output.undigested, 2048);
1066 assert_eq!(output.principals.len(), 4);
1067
1068 let principals: HashMap<u64, PrincipalSummary> =
1069 output.principals.into_iter().map(|p| (p.id, p)).collect();
1070
1071 assert_eq!(
1072 principals.get(&0).unwrap(),
1073 &PrincipalSummary {
1074 id: 0,
1075 name: "root".to_owned(),
1076 principal_type: "R".to_owned(),
1077 committed_private: 1024,
1078 committed_scaled: 1536.0,
1079 committed_total: 2048,
1080 populated_private: 2048,
1081 populated_scaled: 3072.0,
1082 populated_total: 4096,
1083 attributor: None,
1084 processes: vec!["root_process (1001)".to_owned()],
1085 vmos: vec![
1086 (
1087 ZXName::from_string_lossy("root_vmo"),
1088 VmoSummary {
1089 count: 1,
1090 committed_private: 1024,
1091 committed_scaled: 1024.0,
1092 committed_total: 1024,
1093 populated_private: 2048,
1094 populated_scaled: 2048.0,
1095 populated_total: 2048,
1096 ..Default::default()
1097 }
1098 ),
1099 (
1100 ZXName::from_string_lossy("shared_vmo"),
1101 VmoSummary {
1102 count: 1,
1103 committed_private: 0,
1104 committed_scaled: 512.0,
1105 committed_total: 1024,
1106 populated_private: 0,
1107 populated_scaled: 1024.0,
1108 populated_total: 2048,
1109 ..Default::default()
1110 }
1111 )
1112 ]
1113 .into_iter()
1114 .collect(),
1115 }
1116 );
1117
1118 assert_eq!(
1119 principals.get(&1).unwrap(),
1120 &PrincipalSummary {
1121 id: 1,
1122 name: "runner".to_owned(),
1123 principal_type: "R".to_owned(),
1124 committed_private: 1024,
1125 committed_scaled: 1024.0,
1126 committed_total: 1024,
1127 populated_private: 2048,
1128 populated_scaled: 2048.0,
1129 populated_total: 2048,
1130 attributor: Some("root".to_owned()),
1131 processes: vec!["runner_process (1005)".to_owned()],
1132 vmos: vec![(
1133 ZXName::from_string_lossy("runner_vmo"),
1134 VmoSummary {
1135 count: 1,
1136 committed_private: 1024,
1137 committed_scaled: 1024.0,
1138 committed_total: 1024,
1139 populated_private: 2048,
1140 populated_scaled: 2048.0,
1141 populated_total: 2048,
1142 ..Default::default()
1143 }
1144 )]
1145 .into_iter()
1146 .collect(),
1147 }
1148 );
1149
1150 assert_eq!(
1151 principals.get(&2).unwrap(),
1152 &PrincipalSummary {
1153 id: 2,
1154 name: "component 2".to_owned(),
1155 principal_type: "R".to_owned(),
1156 committed_private: 1024,
1157 committed_scaled: 1536.0,
1158 committed_total: 2048,
1159 populated_private: 2048,
1160 populated_scaled: 3072.0,
1161 populated_total: 4096,
1162 attributor: Some("root".to_owned()),
1163 processes: vec!["2_process (1009)".to_owned()],
1164 vmos: vec![
1165 (
1166 ZXName::from_string_lossy("shared_vmo"),
1167 VmoSummary {
1168 count: 1,
1169 committed_private: 0,
1170 committed_scaled: 512.0,
1171 committed_total: 1024,
1172 populated_private: 0,
1173 populated_scaled: 1024.0,
1174 populated_total: 2048,
1175 ..Default::default()
1176 }
1177 ),
1178 (
1179 ZXName::from_string_lossy("2_vmo"),
1180 VmoSummary {
1181 count: 1,
1182 committed_private: 1024,
1183 committed_scaled: 1024.0,
1184 committed_total: 1024,
1185 populated_private: 2048,
1186 populated_scaled: 2048.0,
1187 populated_total: 2048,
1188 ..Default::default()
1189 }
1190 )
1191 ]
1192 .into_iter()
1193 .collect(),
1194 }
1195 );
1196
1197 assert_eq!(
1198 principals.get(&3).unwrap(),
1199 &PrincipalSummary {
1200 id: 3,
1201 name: "component 3".to_owned(),
1202 principal_type: "R".to_owned(),
1203 committed_private: 2176,
1204 committed_scaled: 2176.0,
1205 committed_total: 2176,
1206 populated_private: 4352,
1207 populated_scaled: 4352.0,
1208 populated_total: 4352,
1209 attributor: Some("runner".to_owned()),
1210 processes: vec!["runner_process (1005)".to_owned()],
1211 vmos: vec![
1212 (
1213 ZXName::from_string_lossy("component_vmo"),
1214 VmoSummary {
1215 count: 1,
1216 committed_private: 128,
1217 committed_scaled: 128.0,
1218 committed_total: 128,
1219 populated_private: 256,
1220 populated_scaled: 256.0,
1221 populated_total: 256,
1222 ..Default::default()
1223 }
1224 ),
1225 (
1226 ZXName::from_string_lossy("component_vmo_mapped"),
1227 VmoSummary {
1228 count: 1,
1229 committed_private: 1024,
1230 committed_scaled: 1024.0,
1231 committed_total: 1024,
1232 populated_private: 2048,
1233 populated_scaled: 2048.0,
1234 populated_total: 2048,
1235 ..Default::default()
1236 }
1237 ),
1238 (
1239 ZXName::from_string_lossy("component_vmo_mapped2"),
1240 VmoSummary {
1241 count: 1,
1242 committed_private: 1024,
1243 committed_scaled: 1024.0,
1244 committed_total: 1024,
1245 populated_private: 2048,
1246 populated_scaled: 2048.0,
1247 populated_total: 2048,
1248 ..Default::default()
1249 }
1250 )
1251 ]
1252 .into_iter()
1253 .collect(),
1254 }
1255 );
1256 }
1257
1258 #[test]
1259 fn test_reshare_resources() {
1260 let resource_names = vec![
1274 ZXName::from_string_lossy("root_job"),
1275 ZXName::from_string_lossy("component_job"),
1276 ZXName::from_string_lossy("component_process"),
1277 ZXName::from_string_lossy("component_vmo"),
1278 ];
1279 let attributions = vec![
1280 fplugin::Attribution {
1281 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1282 subject: Some(fplugin::PrincipalIdentifier { id: 0 }),
1283 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
1284 ..Default::default()
1285 },
1286 fplugin::Attribution {
1287 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1288 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
1289 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1290 ..Default::default()
1291 },
1292 fplugin::Attribution {
1293 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
1294 subject: Some(fplugin::PrincipalIdentifier { id: 2 }),
1295 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1296 ..Default::default()
1297 },
1298 ]
1299 .into_iter()
1300 .map(|a| a.into())
1301 .collect();
1302 let principals = vec![
1303 fplugin::Principal {
1304 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
1305 description: Some(fplugin::Description::Component("root".to_owned())),
1306 principal_type: Some(fplugin::PrincipalType::Runnable),
1307 parent: None,
1308 ..Default::default()
1309 },
1310 fplugin::Principal {
1311 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
1312 description: Some(fplugin::Description::Component("component 1".to_owned())),
1313 principal_type: Some(fplugin::PrincipalType::Runnable),
1314 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
1315 ..Default::default()
1316 },
1317 fplugin::Principal {
1318 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
1319 description: Some(fplugin::Description::Component("component 2".to_owned())),
1320 principal_type: Some(fplugin::PrincipalType::Runnable),
1321 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
1322 ..Default::default()
1323 },
1324 ]
1325 .into_iter()
1326 .map(|p| p.into())
1327 .collect();
1328
1329 let resources = vec![
1330 fplugin::Resource {
1331 koid: Some(1000),
1332 name_index: Some(0),
1333 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1334 child_jobs: Some(vec![1001]),
1335 processes: Some(vec![]),
1336 ..Default::default()
1337 })),
1338 ..Default::default()
1339 },
1340 fplugin::Resource {
1341 koid: Some(1001),
1342 name_index: Some(1),
1343 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1344 child_jobs: Some(vec![]),
1345 processes: Some(vec![1002]),
1346 ..Default::default()
1347 })),
1348 ..Default::default()
1349 },
1350 fplugin::Resource {
1351 koid: Some(1002),
1352 name_index: Some(2),
1353 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1354 vmos: Some(vec![1003]),
1355 mappings: None,
1356 ..Default::default()
1357 })),
1358 ..Default::default()
1359 },
1360 fplugin::Resource {
1361 koid: Some(1003),
1362 name_index: Some(3),
1363 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1364 private_committed_bytes: Some(1024),
1365 private_populated_bytes: Some(2048),
1366 scaled_committed_bytes: Some(1024),
1367 scaled_populated_bytes: Some(2048),
1368 total_committed_bytes: Some(1024),
1369 total_populated_bytes: Some(2048),
1370 ..Default::default()
1371 })),
1372 ..Default::default()
1373 },
1374 ]
1375 .into_iter()
1376 .map(|r| r.into())
1377 .collect();
1378
1379 let output = attribute_vmos(AttributionData {
1380 principals_vec: principals,
1381 resources_vec: resources,
1382 resource_names,
1383 attributions,
1384 })
1385 .summary();
1386
1387 assert_eq!(output.undigested, 0);
1388 assert_eq!(output.principals.len(), 3);
1389
1390 let principals: HashMap<u64, PrincipalSummary> =
1391 output.principals.into_iter().map(|p| (p.id, p)).collect();
1392
1393 assert_eq!(
1394 principals.get(&0).unwrap(),
1395 &PrincipalSummary {
1396 id: 0,
1397 name: "root".to_owned(),
1398 principal_type: "R".to_owned(),
1399 committed_private: 0,
1400 committed_scaled: 0.0,
1401 committed_total: 0,
1402 populated_private: 0,
1403 populated_scaled: 0.0,
1404 populated_total: 0,
1405 attributor: None,
1406 processes: vec![],
1407 vmos: vec![].into_iter().collect(),
1408 }
1409 );
1410
1411 assert_eq!(
1412 principals.get(&1).unwrap(),
1413 &PrincipalSummary {
1414 id: 1,
1415 name: "component 1".to_owned(),
1416 principal_type: "R".to_owned(),
1417 committed_private: 0,
1418 committed_scaled: 0.0,
1419 committed_total: 0,
1420 populated_private: 0,
1421 populated_scaled: 0.0,
1422 populated_total: 0,
1423 attributor: Some("root".to_owned()),
1424 processes: vec![],
1425 vmos: vec![].into_iter().collect(),
1426 }
1427 );
1428
1429 assert_eq!(
1430 principals.get(&2).unwrap(),
1431 &PrincipalSummary {
1432 id: 2,
1433 name: "component 2".to_owned(),
1434 principal_type: "R".to_owned(),
1435 committed_private: 1024,
1436 committed_scaled: 1024.0,
1437 committed_total: 1024,
1438 populated_private: 2048,
1439 populated_scaled: 2048.0,
1440 populated_total: 2048,
1441 attributor: Some("component 1".to_owned()),
1442 processes: vec!["component_process (1002)".to_owned()],
1443 vmos: vec![(
1444 ZXName::from_string_lossy("component_vmo"),
1445 VmoSummary {
1446 count: 1,
1447 committed_private: 1024,
1448 committed_scaled: 1024.0,
1449 committed_total: 1024,
1450 populated_private: 2048,
1451 populated_scaled: 2048.0,
1452 populated_total: 2048,
1453 ..Default::default()
1454 }
1455 ),]
1456 .into_iter()
1457 .collect(),
1458 }
1459 );
1460 }
1461
1462 #[test]
1463 fn test_conversions() {
1464 let plugin_principal = fplugin::Principal {
1465 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
1466 description: Some(fplugin::Description::Component("root".to_owned())),
1467 principal_type: Some(fplugin::PrincipalType::Runnable),
1468 parent: None,
1469 ..Default::default()
1470 };
1471
1472 let data_principal: Principal = plugin_principal.clone().into();
1473
1474 assert_eq!(plugin_principal, data_principal.into());
1475
1476 let plugin_resources = vec![
1477 fplugin::Resource {
1478 koid: Some(1000),
1479 name_index: Some(0),
1480 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1481 child_jobs: Some(vec![1004, 1008]),
1482 processes: Some(vec![1001]),
1483 ..Default::default()
1484 })),
1485 ..Default::default()
1486 },
1487 fplugin::Resource {
1488 koid: Some(1001),
1489 name_index: Some(1),
1490 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1491 vmos: Some(vec![1002, 1003]),
1492 mappings: None,
1493 ..Default::default()
1494 })),
1495 ..Default::default()
1496 },
1497 fplugin::Resource {
1498 koid: Some(1002),
1499 name_index: Some(2),
1500 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1501 private_committed_bytes: Some(1024),
1502 private_populated_bytes: Some(2048),
1503 scaled_committed_bytes: Some(1024),
1504 scaled_populated_bytes: Some(2048),
1505 total_committed_bytes: Some(1024),
1506 total_populated_bytes: Some(2048),
1507 ..Default::default()
1508 })),
1509 ..Default::default()
1510 },
1511 fplugin::Resource {
1512 koid: Some(1005),
1513 name_index: Some(5),
1514 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1515 vmos: Some(vec![1006, 1007, 1012]),
1516 mappings: Some(vec![
1517 fplugin::Mapping {
1518 vmo: Some(1006),
1519 address_base: Some(0),
1520 size: Some(512),
1521 ..Default::default()
1522 },
1523 fplugin::Mapping {
1524 vmo: Some(1012),
1525 address_base: Some(1024),
1526 size: Some(512),
1527 ..Default::default()
1528 },
1529 ]),
1530 ..Default::default()
1531 })),
1532 ..Default::default()
1533 },
1534 ];
1535
1536 let data_resources: Vec<Resource> =
1537 plugin_resources.iter().cloned().map(|r| r.into()).collect();
1538
1539 let actual_resources: Vec<fplugin::Resource> =
1540 data_resources.into_iter().map(|r| r.into()).collect();
1541
1542 assert_eq!(plugin_resources, actual_resources);
1543 }
1544
1545 #[test]
1546 fn test_vmo_reference() {
1547 let resource_names = vec![
1562 name::ZXName::from_string_lossy("root_job"),
1563 name::ZXName::from_string_lossy("component_job"),
1564 name::ZXName::from_string_lossy("component_process"),
1565 name::ZXName::from_string_lossy("component_vmo"),
1566 name::ZXName::from_string_lossy("component_vmo_parent"),
1567 ];
1568 let attributions = vec![
1569 fplugin::Attribution {
1570 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1571 subject: Some(fplugin::PrincipalIdentifier { id: 0 }),
1572 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
1573 ..Default::default()
1574 },
1575 fplugin::Attribution {
1576 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1577 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
1578 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1579 ..Default::default()
1580 },
1581 ]
1582 .into_iter()
1583 .map(|a| a.into())
1584 .collect();
1585 let principals = vec![
1586 fplugin::Principal {
1587 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
1588 description: Some(fplugin::Description::Component("root".to_owned())),
1589 principal_type: Some(fplugin::PrincipalType::Runnable),
1590 parent: None,
1591 ..Default::default()
1592 },
1593 fplugin::Principal {
1594 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
1595 description: Some(fplugin::Description::Component("component 1".to_owned())),
1596 principal_type: Some(fplugin::PrincipalType::Runnable),
1597 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
1598 ..Default::default()
1599 },
1600 ]
1601 .into_iter()
1602 .map(|p| p.into())
1603 .collect();
1604
1605 let resources = vec![
1606 fplugin::Resource {
1607 koid: Some(1000),
1608 name_index: Some(0),
1609 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1610 child_jobs: Some(vec![1001]),
1611 processes: Some(vec![]),
1612 ..Default::default()
1613 })),
1614 ..Default::default()
1615 },
1616 fplugin::Resource {
1617 koid: Some(1001),
1618 name_index: Some(1),
1619 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1620 child_jobs: Some(vec![]),
1621 processes: Some(vec![1002]),
1622 ..Default::default()
1623 })),
1624 ..Default::default()
1625 },
1626 fplugin::Resource {
1627 koid: Some(1002),
1628 name_index: Some(2),
1629 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1630 vmos: Some(vec![1003]),
1631 mappings: None,
1632 ..Default::default()
1633 })),
1634 ..Default::default()
1635 },
1636 fplugin::Resource {
1637 koid: Some(1003),
1638 name_index: Some(3),
1639 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1640 parent: Some(1004),
1641 private_committed_bytes: Some(0),
1642 private_populated_bytes: Some(0),
1643 scaled_committed_bytes: Some(0),
1644 scaled_populated_bytes: Some(0),
1645 total_committed_bytes: Some(0),
1646 total_populated_bytes: Some(0),
1647 ..Default::default()
1648 })),
1649 ..Default::default()
1650 },
1651 fplugin::Resource {
1652 koid: Some(1004),
1653 name_index: Some(4),
1654 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1655 private_committed_bytes: Some(1024),
1656 private_populated_bytes: Some(2048),
1657 scaled_committed_bytes: Some(1024),
1658 scaled_populated_bytes: Some(2048),
1659 total_committed_bytes: Some(1024),
1660 total_populated_bytes: Some(2048),
1661 ..Default::default()
1662 })),
1663 ..Default::default()
1664 },
1665 ]
1666 .into_iter()
1667 .map(|r| r.into())
1668 .collect();
1669
1670 let output = attribute_vmos(AttributionData {
1671 principals_vec: principals,
1672 resources_vec: resources,
1673 resource_names,
1674 attributions,
1675 })
1676 .summary();
1677
1678 assert_eq!(output.undigested, 0);
1679 assert_eq!(output.principals.len(), 2);
1680
1681 let principals: HashMap<u64, PrincipalSummary> =
1682 output.principals.into_iter().map(|p| (p.id, p)).collect();
1683
1684 assert_eq!(
1685 principals.get(&0).unwrap(),
1686 &PrincipalSummary {
1687 id: 0,
1688 name: "root".to_owned(),
1689 principal_type: "R".to_owned(),
1690 committed_private: 0,
1691 committed_scaled: 0.0,
1692 committed_total: 0,
1693 populated_private: 0,
1694 populated_scaled: 0.0,
1695 populated_total: 0,
1696 attributor: None,
1697 processes: vec![],
1698 vmos: vec![].into_iter().collect(),
1699 }
1700 );
1701
1702 assert_eq!(
1703 principals.get(&1).unwrap(),
1704 &PrincipalSummary {
1705 id: 1,
1706 name: "component 1".to_owned(),
1707 principal_type: "R".to_owned(),
1708 committed_private: 1024,
1709 committed_scaled: 1024.0,
1710 committed_total: 1024,
1711 populated_private: 2048,
1712 populated_scaled: 2048.0,
1713 populated_total: 2048,
1714 attributor: Some("root".to_owned()),
1715 processes: vec!["component_process (1002)".to_owned()],
1716 vmos: vec![
1717 (
1718 name::ZXName::from_string_lossy("component_vmo"),
1719 VmoSummary {
1720 count: 1,
1721 committed_private: 0,
1722 committed_scaled: 0.0,
1723 committed_total: 0,
1724 populated_private: 0,
1725 populated_scaled: 0.0,
1726 populated_total: 0,
1727 ..Default::default()
1728 }
1729 ),
1730 (
1731 name::ZXName::from_string_lossy("component_vmo_parent"),
1732 VmoSummary {
1733 count: 1,
1734 committed_private: 1024,
1735 committed_scaled: 1024.0,
1736 committed_total: 1024,
1737 populated_private: 2048,
1738 populated_scaled: 2048.0,
1739 populated_total: 2048,
1740 ..Default::default()
1741 }
1742 ),
1743 ]
1744 .into_iter()
1745 .collect(),
1746 }
1747 );
1748 }
1749}