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