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