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