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