1use core::cell::RefCell;
6use core::convert::Into;
7use fidl_fuchsia_memory_attribution_plugin as fplugin;
8use serde::Serialize;
9use std::collections::{HashMap, HashSet};
10use summary::MemorySummary;
11
12mod name;
13
14pub mod digest;
15pub mod fkernel_serde;
16pub mod fplugin_serde;
17pub use name::ZXName;
18pub mod summary;
19
20#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Serialize)]
21pub struct PrincipalIdentifier(pub u64);
22
23impl From<fplugin::PrincipalIdentifier> for PrincipalIdentifier {
24 fn from(value: fplugin::PrincipalIdentifier) -> Self {
25 PrincipalIdentifier(value.id)
26 }
27}
28
29impl Into<fplugin::PrincipalIdentifier> for PrincipalIdentifier {
30 fn into(self) -> fplugin::PrincipalIdentifier {
31 fplugin::PrincipalIdentifier { id: self.0 }
32 }
33}
34
35#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize)]
37pub enum PrincipalDescription {
38 Component(String),
39 Part(String),
40}
41
42impl From<fplugin::Description> for PrincipalDescription {
43 fn from(value: fplugin::Description) -> Self {
44 match value {
45 fplugin::Description::Component(s) => PrincipalDescription::Component(s),
46 fplugin::Description::Part(s) => PrincipalDescription::Part(s),
47 _ => unreachable!(),
48 }
49 }
50}
51
52impl Into<fplugin::Description> for PrincipalDescription {
53 fn into(self) -> fplugin::Description {
54 match self {
55 PrincipalDescription::Component(s) => fplugin::Description::Component(s),
56 PrincipalDescription::Part(s) => fplugin::Description::Part(s),
57 }
58 }
59}
60
61#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize)]
63pub enum PrincipalType {
64 Runnable,
65 Part,
66}
67
68impl From<fplugin::PrincipalType> for PrincipalType {
69 fn from(value: fplugin::PrincipalType) -> Self {
70 match value {
71 fplugin::PrincipalType::Runnable => PrincipalType::Runnable,
72 fplugin::PrincipalType::Part => PrincipalType::Part,
73 _ => unreachable!(),
74 }
75 }
76}
77
78impl Into<fplugin::PrincipalType> for PrincipalType {
79 fn into(self) -> fplugin::PrincipalType {
80 match self {
81 PrincipalType::Runnable => fplugin::PrincipalType::Runnable,
82 PrincipalType::Part => fplugin::PrincipalType::Part,
83 }
84 }
85}
86
87#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
88pub struct Principal {
90 pub identifier: PrincipalIdentifier,
92 pub description: Option<PrincipalDescription>,
93 pub principal_type: PrincipalType,
94
95 pub parent: Option<PrincipalIdentifier>,
99}
100
101impl From<fplugin::Principal> for Principal {
104 fn from(value: fplugin::Principal) -> Self {
105 Principal {
106 identifier: value.identifier.unwrap().into(),
107 description: value.description.map(Into::into),
108 principal_type: value.principal_type.unwrap().into(),
109 parent: value.parent.map(Into::into),
110 }
111 }
112}
113
114impl Into<fplugin::Principal> for Principal {
115 fn into(self) -> fplugin::Principal {
116 fplugin::Principal {
117 identifier: Some(self.identifier.into()),
118 description: self.description.map(Into::into),
119 principal_type: Some(self.principal_type.into()),
120 parent: self.parent.map(Into::into),
121 ..Default::default()
122 }
123 }
124}
125
126#[derive(Serialize)]
128pub struct InflatedPrincipal {
129 principal: Principal,
131
132 attribution_claims: HashMap<PrincipalIdentifier, Attribution>,
136 resources: HashSet<u64>,
139}
140
141impl InflatedPrincipal {
142 fn new(principal: Principal) -> InflatedPrincipal {
143 InflatedPrincipal {
144 principal,
145 attribution_claims: Default::default(),
146 resources: Default::default(),
147 }
148 }
149}
150
151impl InflatedPrincipal {
152 fn name(&self) -> &str {
153 match &self.principal.description {
154 Some(PrincipalDescription::Component(component_name)) => component_name,
155 Some(PrincipalDescription::Part(part_name)) => part_name,
156 None => "?",
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 trait ResourcesVisitor {
421 fn on_job(
422 &mut self,
423 job_koid: zx_types::zx_koid_t,
424 job_name: &ZXName,
425 job: fplugin::Job,
426 ) -> Result<(), zx_status::Status>;
427 fn on_process(
428 &mut self,
429 process_koid: zx_types::zx_koid_t,
430 process_name: &ZXName,
431 process: fplugin::Process,
432 ) -> Result<(), zx_status::Status>;
433 fn on_vmo(
434 &mut self,
435 vmo_koid: zx_types::zx_koid_t,
436 vmo_name: &ZXName,
437 vmo: fplugin::Vmo,
438 ) -> Result<(), zx_status::Status>;
439}
440
441pub trait ResourceEnumerator {
442 fn for_each_resource(&self, visitor: &mut impl ResourcesVisitor) -> Result<(), anyhow::Error>;
444}
445
446pub struct AttributionData {
449 pub principals_vec: Vec<Principal>,
450 pub resources_vec: Vec<Resource>,
451 pub resource_names: Vec<ZXName>,
452 pub attributions: Vec<Attribution>,
453}
454
455impl ResourceEnumerator for AttributionData {
456 fn for_each_resource(&self, visitor: &mut impl ResourcesVisitor) -> Result<(), anyhow::Error> {
457 for resource in &self.resources_vec {
458 if let Resource {
459 koid,
460 name_index,
461 resource_type: fplugin::ResourceType::Vmo(vmo),
462 ..
463 } = &resource
464 {
465 visitor.on_vmo(*koid, &self.resource_names[*name_index], vmo.clone())?;
466 }
467 }
468 for resource in &self.resources_vec {
469 if let Resource {
470 koid,
471 name_index,
472 resource_type: fplugin::ResourceType::Process(process),
473 ..
474 } = &resource
475 {
476 visitor.on_process(*koid, &self.resource_names[*name_index], process.clone())?;
477 }
478 }
479 for resource in &self.resources_vec {
480 if let Resource {
481 koid,
482 name_index,
483 resource_type: fplugin::ResourceType::Job(job),
484 ..
485 } = &resource
486 {
487 visitor.on_job(*koid, &self.resource_names[*name_index], job.clone())?;
488 }
489 }
490 Ok(())
491 }
492}
493
494pub trait AttributionDataProvider: ResourceEnumerator + Send + Sync {
495 fn get_attribution_data(&self) -> Result<AttributionData, anyhow::Error>;
497}
498
499pub struct ProcessedAttributionData {
502 pub principals: HashMap<PrincipalIdentifier, RefCell<InflatedPrincipal>>,
503 pub resources: HashMap<u64, RefCell<InflatedResource>>,
504 pub resource_names: Vec<ZXName>,
505}
506
507impl ProcessedAttributionData {
508 fn new(
509 principals: HashMap<PrincipalIdentifier, RefCell<InflatedPrincipal>>,
510 resources: HashMap<u64, RefCell<InflatedResource>>,
511 resource_names: Vec<ZXName>,
512 ) -> Self {
513 Self { principals, resources, resource_names }
514 }
515
516 pub fn summary(&self) -> MemorySummary {
518 MemorySummary::build(&self.principals, &self.resources, &self.resource_names)
519 }
520}
521
522pub fn attribute_vmos(attribution_data: AttributionData) -> ProcessedAttributionData {
524 let principals: HashMap<PrincipalIdentifier, RefCell<InflatedPrincipal>> = attribution_data
526 .principals_vec
527 .into_iter()
528 .map(|p| (p.identifier.clone(), RefCell::new(InflatedPrincipal::new(p))))
529 .collect();
530
531 let mut resources: HashMap<u64, RefCell<InflatedResource>> = attribution_data
533 .resources_vec
534 .into_iter()
535 .map(|r| (r.koid, RefCell::new(InflatedResource::new(r))))
536 .collect();
537
538 for attribution in attribution_data.attributions {
540 principals.get(&attribution.subject.clone().into()).map(|p| {
541 p.borrow_mut().attribution_claims.insert(attribution.source.into(), attribution.clone())
542 });
543 for resource in attribution.resources {
544 match resource {
545 ResourceReference::KernelObject(koid) => {
546 if !resources.contains_key(&koid) {
547 continue;
548 }
549 resources.get_mut(&koid).unwrap().get_mut().claims.insert(Claim {
550 source: attribution.source.into(),
551 subject: attribution.subject.into(),
552 claim_type: ClaimType::Direct,
553 });
554 }
555 ResourceReference::ProcessMapped { process, base, len } => {
556 if !resources.contains_key(&process) {
557 continue;
558 }
559 let mut matched_vmos = Vec::new();
560 if let fplugin::ResourceType::Process(process_data) =
561 &resources.get(&process).unwrap().borrow().resource.resource_type
562 {
563 for mapping in process_data.mappings.iter().flatten() {
564 if mapping.address_base.unwrap() >= base
567 && mapping.address_base.unwrap() + mapping.size.unwrap()
568 <= base + len
569 {
570 matched_vmos.push(mapping.vmo.unwrap());
571 }
572 }
573 }
574 for vmo_koid in matched_vmos {
575 match resources.get_mut(&vmo_koid) {
576 Some(resource) => {
577 resource.get_mut().claims.insert(Claim {
578 source: attribution.source.into(),
579 subject: attribution.subject.into(),
580 claim_type: ClaimType::Direct,
581 });
582 }
583 None => {
584 }
588 }
589 }
590 }
591 }
592 }
593 }
594
595 for (_, resource_refcell) in &resources {
600 let resource = resource_refcell.borrow_mut();
601 let direct_claims: Vec<&Claim> = resource
603 .claims
604 .iter()
605 .filter(|claim| match claim.claim_type {
606 ClaimType::Direct => true,
607 _ => false,
608 })
609 .collect();
610
611 if direct_claims.is_empty() {
612 continue;
614 }
615
616 let propagated_claims: Vec<Claim> = direct_claims
617 .into_iter()
618 .map(|claim| Claim {
619 source: claim.source,
620 subject: claim.subject,
621 claim_type: ClaimType::Indirect,
622 })
623 .collect();
624 let mut frontier = Vec::new();
625 frontier.extend(resource.children());
626 while !frontier.is_empty() {
627 let child = frontier.pop().unwrap();
628 let mut child_resource = match resources.get(&child) {
629 Some(resource) => resource.borrow_mut(),
630 None => {
631 continue;
635 }
636 };
637 if child_resource.claims.iter().any(|c| c.claim_type == ClaimType::Direct) {
638 continue;
640 }
641 child_resource.claims.extend(propagated_claims.clone().iter());
642 frontier.extend(child_resource.children().iter());
643 }
644 }
645
646 for (_, resource_refcell) in &resources {
647 let mut resource = resource_refcell.borrow_mut();
648 resource.process_claims();
649 }
650
651 for (resource_id, resource_refcell) in &resources {
654 let resource = resource_refcell.borrow();
655 if let fplugin::ResourceType::Vmo(vmo) = &resource.resource.resource_type {
656 let mut ancestors = vec![*resource_id];
657 if vmo.total_populated_bytes.unwrap_or_default() == 0 {
664 let mut current_parent = vmo.parent;
665 while let Some(parent_koid) = current_parent {
669 if parent_koid == 0 {
670 panic!("Parent is not None but 0.");
671 }
672 if parent_koid == resource.resource.koid {
676 break;
677 }
678 ancestors.push(parent_koid);
679 let mut current_resource = match resources.get(&parent_koid) {
680 Some(res) => res.borrow_mut(),
681 None => break,
682 };
683 current_resource.claims.extend(resource.claims.iter().map(|c| Claim {
684 subject: c.subject,
685 source: c.source,
686 claim_type: ClaimType::Child,
687 }));
688 current_parent = match ¤t_resource.resource.resource_type {
689 fplugin::ResourceType::Job(_) => panic!("This should not happen"),
690 fplugin::ResourceType::Process(_) => panic!("This should not happen"),
691 fplugin::ResourceType::Vmo(current_vmo) => current_vmo.parent,
692 _ => unimplemented!(),
693 };
694 }
695 }
696
697 for claim in &resource.claims {
698 principals
699 .get(&claim.subject)
700 .unwrap()
701 .borrow_mut()
702 .resources
703 .extend(ancestors.iter());
704 }
705 } else if let fplugin::ResourceType::Process(_) = &resource.resource.resource_type {
706 for claim in &resource.claims {
707 principals
708 .get(&claim.subject)
709 .unwrap()
710 .borrow_mut()
711 .resources
712 .insert(resource.resource.koid);
713 }
714 }
715 }
716
717 ProcessedAttributionData::new(principals, resources, attribution_data.resource_names)
718}
719
720#[cfg(test)]
721mod tests {
722 use super::*;
723 use std::collections::HashMap;
724 use summary::{PrincipalSummary, VmoSummary};
725
726 #[test]
727 fn test_gather_resources() {
728 let resource_names = vec![
752 ZXName::from_string_lossy("root_job"),
753 ZXName::from_string_lossy("root_process"),
754 ZXName::from_string_lossy("root_vmo"),
755 ZXName::from_string_lossy("shared_vmo"),
756 ZXName::from_string_lossy("runner_job"),
757 ZXName::from_string_lossy("runner_process"),
758 ZXName::from_string_lossy("runner_vmo"),
759 ZXName::from_string_lossy("component_vmo"),
760 ZXName::from_string_lossy("component_2_job"),
761 ZXName::from_string_lossy("2_process"),
762 ZXName::from_string_lossy("2_vmo"),
763 ZXName::from_string_lossy("2_vmo_parent"),
764 ZXName::from_string_lossy("component_vmo_mapped"),
765 ZXName::from_string_lossy("component_vmo_mapped2"),
766 ];
767
768 let attributions = vec![
769 fplugin::Attribution {
770 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
771 subject: Some(fplugin::PrincipalIdentifier { id: 0 }),
772 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
773 ..Default::default()
774 },
775 fplugin::Attribution {
776 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
777 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
778 resources: Some(vec![fplugin::ResourceReference::KernelObject(1004)]),
779 ..Default::default()
780 },
781 fplugin::Attribution {
782 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
783 subject: Some(fplugin::PrincipalIdentifier { id: 2 }),
784 resources: Some(vec![fplugin::ResourceReference::KernelObject(1008)]),
785 ..Default::default()
786 },
787 fplugin::Attribution {
788 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
789 subject: Some(fplugin::PrincipalIdentifier { id: 3 }),
790 resources: Some(vec![
791 fplugin::ResourceReference::KernelObject(1007),
792 fplugin::ResourceReference::ProcessMapped(fplugin::ProcessMapped {
793 process: 1005,
794 base: 1024,
795 len: 1024,
796 }),
797 ]),
798 ..Default::default()
799 },
800 ]
801 .into_iter()
802 .map(|a| a.into())
803 .collect();
804
805 let principals = vec![
806 fplugin::Principal {
807 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
808 description: Some(fplugin::Description::Component("component_manager".to_owned())),
809 principal_type: Some(fplugin::PrincipalType::Runnable),
810 parent: None,
811 ..Default::default()
812 },
813 fplugin::Principal {
814 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
815 description: Some(fplugin::Description::Component("runner".to_owned())),
816 principal_type: Some(fplugin::PrincipalType::Runnable),
817 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
818 ..Default::default()
819 },
820 fplugin::Principal {
821 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
822 description: Some(fplugin::Description::Component("component 2".to_owned())),
823 principal_type: Some(fplugin::PrincipalType::Runnable),
824 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
825 ..Default::default()
826 },
827 fplugin::Principal {
828 identifier: Some(fplugin::PrincipalIdentifier { id: 3 }),
829 description: Some(fplugin::Description::Component("component 3".to_owned())),
830 principal_type: Some(fplugin::PrincipalType::Runnable),
831 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
832 ..Default::default()
833 },
834 ]
835 .into_iter()
836 .map(|p| p.into())
837 .collect();
838
839 let resources = vec![
840 fplugin::Resource {
841 koid: Some(1000),
842 name_index: Some(0),
843 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
844 child_jobs: Some(vec![1004, 1008]),
845 processes: Some(vec![1001]),
846 ..Default::default()
847 })),
848 ..Default::default()
849 },
850 fplugin::Resource {
851 koid: Some(1001),
852 name_index: Some(1),
853 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
854 vmos: Some(vec![1002, 1003]),
855 mappings: None,
856 ..Default::default()
857 })),
858 ..Default::default()
859 },
860 fplugin::Resource {
861 koid: Some(1002),
862 name_index: Some(2),
863 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
864 private_committed_bytes: Some(1024),
865 private_populated_bytes: Some(2048),
866 scaled_committed_bytes: Some(1024),
867 scaled_populated_bytes: Some(2048),
868 total_committed_bytes: Some(1024),
869 total_populated_bytes: Some(2048),
870 ..Default::default()
871 })),
872 ..Default::default()
873 },
874 fplugin::Resource {
875 koid: Some(1003),
876 name_index: Some(3),
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(1004),
890 name_index: Some(4),
891 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
892 child_jobs: Some(vec![]),
893 processes: Some(vec![1005]),
894 ..Default::default()
895 })),
896 ..Default::default()
897 },
898 fplugin::Resource {
899 koid: Some(1005),
900 name_index: Some(5),
901 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
902 vmos: Some(vec![1006, 1007, 1012]),
903 mappings: Some(vec![
904 fplugin::Mapping {
905 vmo: Some(1006),
906 address_base: Some(0),
907 size: Some(512),
908 ..Default::default()
909 },
910 fplugin::Mapping {
911 vmo: Some(1012),
912 address_base: Some(1024),
913 size: Some(512),
914 ..Default::default()
915 },
916 fplugin::Mapping {
917 vmo: Some(1013),
918 address_base: Some(1536),
919 size: Some(512),
920 ..Default::default()
921 },
922 fplugin::Mapping {
923 vmo: Some(1006),
924 address_base: Some(2048),
925 size: Some(512),
926 ..Default::default()
927 },
928 ]),
929 ..Default::default()
930 })),
931 ..Default::default()
932 },
933 fplugin::Resource {
934 koid: Some(1006),
935 name_index: Some(6),
936 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
937 private_committed_bytes: Some(1024),
938 private_populated_bytes: Some(2048),
939 scaled_committed_bytes: Some(1024),
940 scaled_populated_bytes: Some(2048),
941 total_committed_bytes: Some(1024),
942 total_populated_bytes: Some(2048),
943 ..Default::default()
944 })),
945 ..Default::default()
946 },
947 fplugin::Resource {
948 koid: Some(1007),
949 name_index: Some(7),
950 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
951 private_committed_bytes: Some(128),
952 private_populated_bytes: Some(256),
953 scaled_committed_bytes: Some(128),
954 scaled_populated_bytes: Some(256),
955 total_committed_bytes: Some(128),
956 total_populated_bytes: Some(256),
957 ..Default::default()
958 })),
959 ..Default::default()
960 },
961 fplugin::Resource {
962 koid: Some(1008),
963 name_index: Some(8),
964 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
965 child_jobs: Some(vec![]),
966 processes: Some(vec![1009]),
967 ..Default::default()
968 })),
969 ..Default::default()
970 },
971 fplugin::Resource {
972 koid: Some(1009),
973 name_index: Some(9),
974 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
975 vmos: Some(vec![1010, 1003]),
976 mappings: None,
977 ..Default::default()
978 })),
979 ..Default::default()
980 },
981 fplugin::Resource {
982 koid: Some(1010),
983 name_index: Some(10),
984 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
985 parent: Some(1011),
986 private_committed_bytes: Some(1024),
987 private_populated_bytes: Some(2048),
988 scaled_committed_bytes: Some(1024),
989 scaled_populated_bytes: Some(2048),
990 total_committed_bytes: Some(1024),
991 total_populated_bytes: Some(2048),
992 ..Default::default()
993 })),
994 ..Default::default()
995 },
996 fplugin::Resource {
997 koid: Some(1011),
998 name_index: Some(11),
999 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
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(1012),
1012 name_index: Some(12),
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(1013),
1026 name_index: Some(13),
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 ]
1039 .into_iter()
1040 .map(|r| r.into())
1041 .collect();
1042
1043 let output = attribute_vmos(AttributionData {
1044 principals_vec: principals,
1045 resources_vec: resources,
1046 resource_names,
1047 attributions,
1048 })
1049 .summary();
1050
1051 assert_eq!(output.unclaimed, 2048);
1052 assert_eq!(output.principals.len(), 4);
1053
1054 let principals: HashMap<u64, PrincipalSummary> =
1055 output.principals.into_iter().map(|p| (p.id, p)).collect();
1056
1057 assert_eq!(
1058 principals.get(&0).unwrap(),
1059 &PrincipalSummary {
1060 id: 0,
1061 name: "component_manager".to_owned(),
1062 principal_type: "R".to_owned(),
1063 committed_private: 1024,
1064 committed_scaled: 1536.0,
1065 committed_total: 2048,
1066 populated_private: 2048,
1067 populated_scaled: 3072.0,
1068 populated_total: 4096,
1069 attributor: None,
1070 processes: vec!["root_process (1001)".to_owned()],
1071 vmos: vec![
1072 (
1073 ZXName::from_string_lossy("root_vmo"),
1074 VmoSummary {
1075 count: 1,
1076 committed_private: 1024,
1077 committed_scaled: 1024.0,
1078 committed_total: 1024,
1079 populated_private: 2048,
1080 populated_scaled: 2048.0,
1081 populated_total: 2048,
1082 ..Default::default()
1083 }
1084 ),
1085 (
1086 ZXName::from_string_lossy("shared_vmo"),
1087 VmoSummary {
1088 count: 1,
1089 committed_private: 0,
1090 committed_scaled: 512.0,
1091 committed_total: 1024,
1092 populated_private: 0,
1093 populated_scaled: 1024.0,
1094 populated_total: 2048,
1095 ..Default::default()
1096 }
1097 )
1098 ]
1099 .into_iter()
1100 .collect(),
1101 }
1102 );
1103
1104 assert_eq!(
1105 principals.get(&1).unwrap(),
1106 &PrincipalSummary {
1107 id: 1,
1108 name: "runner".to_owned(),
1109 principal_type: "R".to_owned(),
1110 committed_private: 1024,
1111 committed_scaled: 1024.0,
1112 committed_total: 1024,
1113 populated_private: 2048,
1114 populated_scaled: 2048.0,
1115 populated_total: 2048,
1116 attributor: Some("component_manager".to_owned()),
1117 processes: vec!["runner_process (1005)".to_owned()],
1118 vmos: vec![(
1119 ZXName::from_string_lossy("runner_vmo"),
1120 VmoSummary {
1121 count: 1,
1122 committed_private: 1024,
1123 committed_scaled: 1024.0,
1124 committed_total: 1024,
1125 populated_private: 2048,
1126 populated_scaled: 2048.0,
1127 populated_total: 2048,
1128 ..Default::default()
1129 }
1130 )]
1131 .into_iter()
1132 .collect(),
1133 }
1134 );
1135
1136 assert_eq!(
1137 principals.get(&2).unwrap(),
1138 &PrincipalSummary {
1139 id: 2,
1140 name: "component 2".to_owned(),
1141 principal_type: "R".to_owned(),
1142 committed_private: 1024,
1143 committed_scaled: 1536.0,
1144 committed_total: 2048,
1145 populated_private: 2048,
1146 populated_scaled: 3072.0,
1147 populated_total: 4096,
1148 attributor: Some("component_manager".to_owned()),
1149 processes: vec!["2_process (1009)".to_owned()],
1150 vmos: vec![
1151 (
1152 ZXName::from_string_lossy("shared_vmo"),
1153 VmoSummary {
1154 count: 1,
1155 committed_private: 0,
1156 committed_scaled: 512.0,
1157 committed_total: 1024,
1158 populated_private: 0,
1159 populated_scaled: 1024.0,
1160 populated_total: 2048,
1161 ..Default::default()
1162 }
1163 ),
1164 (
1165 ZXName::from_string_lossy("2_vmo"),
1166 VmoSummary {
1167 count: 1,
1168 committed_private: 1024,
1169 committed_scaled: 1024.0,
1170 committed_total: 1024,
1171 populated_private: 2048,
1172 populated_scaled: 2048.0,
1173 populated_total: 2048,
1174 ..Default::default()
1175 }
1176 )
1177 ]
1178 .into_iter()
1179 .collect(),
1180 }
1181 );
1182
1183 assert_eq!(
1184 principals.get(&3).unwrap(),
1185 &PrincipalSummary {
1186 id: 3,
1187 name: "component 3".to_owned(),
1188 principal_type: "R".to_owned(),
1189 committed_private: 2176,
1190 committed_scaled: 2176.0,
1191 committed_total: 2176,
1192 populated_private: 4352,
1193 populated_scaled: 4352.0,
1194 populated_total: 4352,
1195 attributor: Some("runner".to_owned()),
1196 processes: vec!["runner_process (1005)".to_owned()],
1197 vmos: vec![
1198 (
1199 ZXName::from_string_lossy("component_vmo"),
1200 VmoSummary {
1201 count: 1,
1202 committed_private: 128,
1203 committed_scaled: 128.0,
1204 committed_total: 128,
1205 populated_private: 256,
1206 populated_scaled: 256.0,
1207 populated_total: 256,
1208 ..Default::default()
1209 }
1210 ),
1211 (
1212 ZXName::from_string_lossy("component_vmo_mapped"),
1213 VmoSummary {
1214 count: 1,
1215 committed_private: 1024,
1216 committed_scaled: 1024.0,
1217 committed_total: 1024,
1218 populated_private: 2048,
1219 populated_scaled: 2048.0,
1220 populated_total: 2048,
1221 ..Default::default()
1222 }
1223 ),
1224 (
1225 ZXName::from_string_lossy("component_vmo_mapped2"),
1226 VmoSummary {
1227 count: 1,
1228 committed_private: 1024,
1229 committed_scaled: 1024.0,
1230 committed_total: 1024,
1231 populated_private: 2048,
1232 populated_scaled: 2048.0,
1233 populated_total: 2048,
1234 ..Default::default()
1235 }
1236 )
1237 ]
1238 .into_iter()
1239 .collect(),
1240 }
1241 );
1242 }
1243
1244 #[test]
1245 fn test_reshare_resources() {
1246 let resource_names = vec![
1260 ZXName::from_string_lossy("root_job"),
1261 ZXName::from_string_lossy("component_job"),
1262 ZXName::from_string_lossy("component_process"),
1263 ZXName::from_string_lossy("component_vmo"),
1264 ];
1265 let attributions = vec![
1266 fplugin::Attribution {
1267 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1268 subject: Some(fplugin::PrincipalIdentifier { id: 0 }),
1269 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
1270 ..Default::default()
1271 },
1272 fplugin::Attribution {
1273 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1274 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
1275 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1276 ..Default::default()
1277 },
1278 fplugin::Attribution {
1279 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
1280 subject: Some(fplugin::PrincipalIdentifier { id: 2 }),
1281 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1282 ..Default::default()
1283 },
1284 ]
1285 .into_iter()
1286 .map(|a| a.into())
1287 .collect();
1288 let principals = vec![
1289 fplugin::Principal {
1290 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
1291 description: Some(fplugin::Description::Component("component_manager".to_owned())),
1292 principal_type: Some(fplugin::PrincipalType::Runnable),
1293 parent: None,
1294 ..Default::default()
1295 },
1296 fplugin::Principal {
1297 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
1298 description: Some(fplugin::Description::Component("component 1".to_owned())),
1299 principal_type: Some(fplugin::PrincipalType::Runnable),
1300 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
1301 ..Default::default()
1302 },
1303 fplugin::Principal {
1304 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
1305 description: Some(fplugin::Description::Component("component 2".to_owned())),
1306 principal_type: Some(fplugin::PrincipalType::Runnable),
1307 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
1308 ..Default::default()
1309 },
1310 ]
1311 .into_iter()
1312 .map(|p| p.into())
1313 .collect();
1314
1315 let resources = vec![
1316 fplugin::Resource {
1317 koid: Some(1000),
1318 name_index: Some(0),
1319 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1320 child_jobs: Some(vec![1001]),
1321 processes: Some(vec![]),
1322 ..Default::default()
1323 })),
1324 ..Default::default()
1325 },
1326 fplugin::Resource {
1327 koid: Some(1001),
1328 name_index: Some(1),
1329 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1330 child_jobs: Some(vec![]),
1331 processes: Some(vec![1002]),
1332 ..Default::default()
1333 })),
1334 ..Default::default()
1335 },
1336 fplugin::Resource {
1337 koid: Some(1002),
1338 name_index: Some(2),
1339 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1340 vmos: Some(vec![1003]),
1341 mappings: None,
1342 ..Default::default()
1343 })),
1344 ..Default::default()
1345 },
1346 fplugin::Resource {
1347 koid: Some(1003),
1348 name_index: Some(3),
1349 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1350 private_committed_bytes: Some(1024),
1351 private_populated_bytes: Some(2048),
1352 scaled_committed_bytes: Some(1024),
1353 scaled_populated_bytes: Some(2048),
1354 total_committed_bytes: Some(1024),
1355 total_populated_bytes: Some(2048),
1356 ..Default::default()
1357 })),
1358 ..Default::default()
1359 },
1360 ]
1361 .into_iter()
1362 .map(|r| r.into())
1363 .collect();
1364
1365 let output = attribute_vmos(AttributionData {
1366 principals_vec: principals,
1367 resources_vec: resources,
1368 resource_names,
1369 attributions,
1370 })
1371 .summary();
1372
1373 assert_eq!(output.unclaimed, 0);
1374 assert_eq!(output.principals.len(), 3);
1375
1376 let principals: HashMap<u64, PrincipalSummary> =
1377 output.principals.into_iter().map(|p| (p.id, p)).collect();
1378
1379 assert_eq!(
1380 principals.get(&0).unwrap(),
1381 &PrincipalSummary {
1382 id: 0,
1383 name: "component_manager".to_owned(),
1384 principal_type: "R".to_owned(),
1385 committed_private: 0,
1386 committed_scaled: 0.0,
1387 committed_total: 0,
1388 populated_private: 0,
1389 populated_scaled: 0.0,
1390 populated_total: 0,
1391 attributor: None,
1392 processes: vec![],
1393 vmos: vec![].into_iter().collect(),
1394 }
1395 );
1396
1397 assert_eq!(
1398 principals.get(&1).unwrap(),
1399 &PrincipalSummary {
1400 id: 1,
1401 name: "component 1".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: Some("component_manager".to_owned()),
1410 processes: vec![],
1411 vmos: vec![].into_iter().collect(),
1412 }
1413 );
1414
1415 assert_eq!(
1416 principals.get(&2).unwrap(),
1417 &PrincipalSummary {
1418 id: 2,
1419 name: "component 2".to_owned(),
1420 principal_type: "R".to_owned(),
1421 committed_private: 1024,
1422 committed_scaled: 1024.0,
1423 committed_total: 1024,
1424 populated_private: 2048,
1425 populated_scaled: 2048.0,
1426 populated_total: 2048,
1427 attributor: Some("component 1".to_owned()),
1428 processes: vec!["component_process (1002)".to_owned()],
1429 vmos: vec![(
1430 ZXName::from_string_lossy("component_vmo"),
1431 VmoSummary {
1432 count: 1,
1433 committed_private: 1024,
1434 committed_scaled: 1024.0,
1435 committed_total: 1024,
1436 populated_private: 2048,
1437 populated_scaled: 2048.0,
1438 populated_total: 2048,
1439 ..Default::default()
1440 }
1441 ),]
1442 .into_iter()
1443 .collect(),
1444 }
1445 );
1446 }
1447
1448 #[test]
1449 fn test_conversions() {
1450 let plugin_principal = fplugin::Principal {
1451 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
1452 description: Some(fplugin::Description::Component("component_manager".to_owned())),
1453 principal_type: Some(fplugin::PrincipalType::Runnable),
1454 parent: None,
1455 ..Default::default()
1456 };
1457
1458 let data_principal: Principal = plugin_principal.clone().into();
1459
1460 assert_eq!(plugin_principal, data_principal.into());
1461
1462 let plugin_resources = vec![
1463 fplugin::Resource {
1464 koid: Some(1000),
1465 name_index: Some(0),
1466 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1467 child_jobs: Some(vec![1004, 1008]),
1468 processes: Some(vec![1001]),
1469 ..Default::default()
1470 })),
1471 ..Default::default()
1472 },
1473 fplugin::Resource {
1474 koid: Some(1001),
1475 name_index: Some(1),
1476 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1477 vmos: Some(vec![1002, 1003]),
1478 mappings: None,
1479 ..Default::default()
1480 })),
1481 ..Default::default()
1482 },
1483 fplugin::Resource {
1484 koid: Some(1002),
1485 name_index: Some(2),
1486 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1487 private_committed_bytes: Some(1024),
1488 private_populated_bytes: Some(2048),
1489 scaled_committed_bytes: Some(1024),
1490 scaled_populated_bytes: Some(2048),
1491 total_committed_bytes: Some(1024),
1492 total_populated_bytes: Some(2048),
1493 ..Default::default()
1494 })),
1495 ..Default::default()
1496 },
1497 fplugin::Resource {
1498 koid: Some(1005),
1499 name_index: Some(5),
1500 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1501 vmos: Some(vec![1006, 1007, 1012]),
1502 mappings: Some(vec![
1503 fplugin::Mapping {
1504 vmo: Some(1006),
1505 address_base: Some(0),
1506 size: Some(512),
1507 ..Default::default()
1508 },
1509 fplugin::Mapping {
1510 vmo: Some(1012),
1511 address_base: Some(1024),
1512 size: Some(512),
1513 ..Default::default()
1514 },
1515 ]),
1516 ..Default::default()
1517 })),
1518 ..Default::default()
1519 },
1520 ];
1521
1522 let data_resources: Vec<Resource> =
1523 plugin_resources.iter().cloned().map(|r| r.into()).collect();
1524
1525 let actual_resources: Vec<fplugin::Resource> =
1526 data_resources.into_iter().map(|r| r.into()).collect();
1527
1528 assert_eq!(plugin_resources, actual_resources);
1529 }
1530
1531 #[test]
1532 fn test_vmo_reference() {
1533 let resource_names = vec![
1548 name::ZXName::from_string_lossy("root_job"),
1549 name::ZXName::from_string_lossy("component_job"),
1550 name::ZXName::from_string_lossy("component_process"),
1551 name::ZXName::from_string_lossy("component_vmo"),
1552 name::ZXName::from_string_lossy("component_vmo_parent"),
1553 ];
1554 let attributions = vec![
1555 fplugin::Attribution {
1556 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1557 subject: Some(fplugin::PrincipalIdentifier { id: 0 }),
1558 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
1559 ..Default::default()
1560 },
1561 fplugin::Attribution {
1562 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1563 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
1564 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1565 ..Default::default()
1566 },
1567 ]
1568 .into_iter()
1569 .map(|a| a.into())
1570 .collect();
1571 let principals = vec![
1572 fplugin::Principal {
1573 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
1574 description: Some(fplugin::Description::Component("component_manager".to_owned())),
1575 principal_type: Some(fplugin::PrincipalType::Runnable),
1576 parent: None,
1577 ..Default::default()
1578 },
1579 fplugin::Principal {
1580 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
1581 description: Some(fplugin::Description::Component("component 1".to_owned())),
1582 principal_type: Some(fplugin::PrincipalType::Runnable),
1583 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
1584 ..Default::default()
1585 },
1586 ]
1587 .into_iter()
1588 .map(|p| p.into())
1589 .collect();
1590
1591 let resources = vec![
1592 fplugin::Resource {
1593 koid: Some(1000),
1594 name_index: Some(0),
1595 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1596 child_jobs: Some(vec![1001]),
1597 processes: Some(vec![]),
1598 ..Default::default()
1599 })),
1600 ..Default::default()
1601 },
1602 fplugin::Resource {
1603 koid: Some(1001),
1604 name_index: Some(1),
1605 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1606 child_jobs: Some(vec![]),
1607 processes: Some(vec![1002]),
1608 ..Default::default()
1609 })),
1610 ..Default::default()
1611 },
1612 fplugin::Resource {
1613 koid: Some(1002),
1614 name_index: Some(2),
1615 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1616 vmos: Some(vec![1003]),
1617 mappings: None,
1618 ..Default::default()
1619 })),
1620 ..Default::default()
1621 },
1622 fplugin::Resource {
1623 koid: Some(1003),
1624 name_index: Some(3),
1625 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1626 parent: Some(1004),
1627 private_committed_bytes: Some(0),
1628 private_populated_bytes: Some(0),
1629 scaled_committed_bytes: Some(0),
1630 scaled_populated_bytes: Some(0),
1631 total_committed_bytes: Some(0),
1632 total_populated_bytes: Some(0),
1633 ..Default::default()
1634 })),
1635 ..Default::default()
1636 },
1637 fplugin::Resource {
1638 koid: Some(1004),
1639 name_index: Some(4),
1640 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1641 private_committed_bytes: Some(1024),
1642 private_populated_bytes: Some(2048),
1643 scaled_committed_bytes: Some(1024),
1644 scaled_populated_bytes: Some(2048),
1645 total_committed_bytes: Some(1024),
1646 total_populated_bytes: Some(2048),
1647 ..Default::default()
1648 })),
1649 ..Default::default()
1650 },
1651 ]
1652 .into_iter()
1653 .map(|r| r.into())
1654 .collect();
1655
1656 let output = attribute_vmos(AttributionData {
1657 principals_vec: principals,
1658 resources_vec: resources,
1659 resource_names,
1660 attributions,
1661 })
1662 .summary();
1663
1664 assert_eq!(output.unclaimed, 0);
1665 assert_eq!(output.principals.len(), 2);
1666
1667 let principals: HashMap<u64, PrincipalSummary> =
1668 output.principals.into_iter().map(|p| (p.id, p)).collect();
1669
1670 assert_eq!(
1671 principals.get(&0).unwrap(),
1672 &PrincipalSummary {
1673 id: 0,
1674 name: "component_manager".to_owned(),
1675 principal_type: "R".to_owned(),
1676 committed_private: 0,
1677 committed_scaled: 0.0,
1678 committed_total: 0,
1679 populated_private: 0,
1680 populated_scaled: 0.0,
1681 populated_total: 0,
1682 attributor: None,
1683 processes: vec![],
1684 vmos: vec![].into_iter().collect(),
1685 }
1686 );
1687
1688 assert_eq!(
1689 principals.get(&1).unwrap(),
1690 &PrincipalSummary {
1691 id: 1,
1692 name: "component 1".to_owned(),
1693 principal_type: "R".to_owned(),
1694 committed_private: 1024,
1695 committed_scaled: 1024.0,
1696 committed_total: 1024,
1697 populated_private: 2048,
1698 populated_scaled: 2048.0,
1699 populated_total: 2048,
1700 attributor: Some("component_manager".to_owned()),
1701 processes: vec!["component_process (1002)".to_owned()],
1702 vmos: vec![
1703 (
1704 name::ZXName::from_string_lossy("component_vmo"),
1705 VmoSummary {
1706 count: 1,
1707 committed_private: 0,
1708 committed_scaled: 0.0,
1709 committed_total: 0,
1710 populated_private: 0,
1711 populated_scaled: 0.0,
1712 populated_total: 0,
1713 ..Default::default()
1714 }
1715 ),
1716 (
1717 name::ZXName::from_string_lossy("component_vmo_parent"),
1718 VmoSummary {
1719 count: 1,
1720 committed_private: 1024,
1721 committed_scaled: 1024.0,
1722 committed_total: 1024,
1723 populated_private: 2048,
1724 populated_scaled: 2048.0,
1725 populated_total: 2048,
1726 ..Default::default()
1727 }
1728 ),
1729 ]
1730 .into_iter()
1731 .collect(),
1732 }
1733 );
1734 }
1735}