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