1use core::cell::RefCell;
6use core::convert::Into;
7use fidl_fuchsia_memory_attribution_plugin as fplugin;
8use futures::future::BoxFuture;
9use std::collections::{HashMap, HashSet};
10use summary::MemorySummary;
11
12pub mod kernel_statistics;
13pub mod summary;
14
15#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
16pub struct PrincipalIdentifier(pub u64);
17
18impl From<fplugin::PrincipalIdentifier> for PrincipalIdentifier {
19 fn from(value: fplugin::PrincipalIdentifier) -> Self {
20 PrincipalIdentifier(value.id)
21 }
22}
23
24impl Into<fplugin::PrincipalIdentifier> for PrincipalIdentifier {
25 fn into(self) -> fplugin::PrincipalIdentifier {
26 fplugin::PrincipalIdentifier { id: self.0 }
27 }
28}
29
30#[derive(PartialEq, Eq, Clone, Debug, Hash)]
32pub enum PrincipalDescription {
33 Component(String),
34 Part(String),
35}
36
37impl From<fplugin::Description> for PrincipalDescription {
38 fn from(value: fplugin::Description) -> Self {
39 match value {
40 fplugin::Description::Component(s) => PrincipalDescription::Component(s),
41 fplugin::Description::Part(s) => PrincipalDescription::Part(s),
42 _ => unreachable!(),
43 }
44 }
45}
46
47impl Into<fplugin::Description> for PrincipalDescription {
48 fn into(self) -> fplugin::Description {
49 match self {
50 PrincipalDescription::Component(s) => fplugin::Description::Component(s),
51 PrincipalDescription::Part(s) => fplugin::Description::Part(s),
52 }
53 }
54}
55
56#[derive(PartialEq, Eq, Clone, Debug, Hash)]
58pub enum PrincipalType {
59 Runnable,
60 Part,
61}
62
63impl From<fplugin::PrincipalType> for PrincipalType {
64 fn from(value: fplugin::PrincipalType) -> Self {
65 match value {
66 fplugin::PrincipalType::Runnable => PrincipalType::Runnable,
67 fplugin::PrincipalType::Part => PrincipalType::Part,
68 _ => unreachable!(),
69 }
70 }
71}
72
73impl Into<fplugin::PrincipalType> for PrincipalType {
74 fn into(self) -> fplugin::PrincipalType {
75 match self {
76 PrincipalType::Runnable => fplugin::PrincipalType::Runnable,
77 PrincipalType::Part => fplugin::PrincipalType::Part,
78 }
79 }
80}
81
82#[derive(PartialEq, Eq, Debug, Hash)]
83pub struct Principal {
85 pub identifier: PrincipalIdentifier,
87 pub description: PrincipalDescription,
88 pub principal_type: PrincipalType,
89
90 pub parent: Option<PrincipalIdentifier>,
94}
95
96impl From<fplugin::Principal> for Principal {
99 fn from(value: fplugin::Principal) -> Self {
100 Principal {
101 identifier: value.identifier.unwrap().into(),
102 description: value.description.unwrap().into(),
103 principal_type: value.principal_type.unwrap().into(),
104 parent: value.parent.map(Into::into),
105 }
106 }
107}
108
109impl Into<fplugin::Principal> for Principal {
110 fn into(self) -> fplugin::Principal {
111 fplugin::Principal {
112 identifier: Some(self.identifier.into()),
113 description: Some(self.description.into()),
114 principal_type: Some(self.principal_type.into()),
115 parent: self.parent.map(Into::into),
116 ..Default::default()
117 }
118 }
119}
120
121pub struct InflatedPrincipal {
123 principal: Principal,
125
126 attribution_claims: HashMap<PrincipalIdentifier, Attribution>,
130 resources: HashSet<u64>,
133}
134
135impl InflatedPrincipal {
136 fn new(principal: Principal) -> InflatedPrincipal {
137 InflatedPrincipal {
138 principal,
139 attribution_claims: Default::default(),
140 resources: Default::default(),
141 }
142 }
143}
144
145impl InflatedPrincipal {
146 fn name(&self) -> &str {
147 match &self.principal.description {
148 PrincipalDescription::Component(component_name) => component_name,
149 PrincipalDescription::Part(part_name) => part_name,
150 }
151 }
152}
153
154#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
156pub enum ClaimType {
157 Direct,
159 Indirect,
162 Child,
164}
165
166#[derive(Clone, Copy, PartialEq, Eq, Hash)]
167pub struct Koid(u64);
168
169impl From<u64> for Koid {
170 fn from(value: u64) -> Self {
171 Koid(value)
172 }
173}
174
175#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
181pub struct Claim {
182 subject: PrincipalIdentifier,
184 source: PrincipalIdentifier,
186 claim_type: ClaimType,
187}
188
189#[derive(Debug, PartialEq)]
190pub struct Resource {
191 pub koid: u64,
192 pub name_index: usize,
193 pub resource_type: fplugin::ResourceType,
194}
195
196impl From<fplugin::Resource> for Resource {
197 fn from(value: fplugin::Resource) -> Self {
198 Resource {
199 koid: value.koid.unwrap(),
200 name_index: value.name_index.unwrap() as usize,
201 resource_type: value.resource_type.unwrap(),
202 }
203 }
204}
205
206impl Into<fplugin::Resource> for Resource {
207 fn into(self) -> fplugin::Resource {
208 fplugin::Resource {
209 koid: Some(self.koid),
210 name_index: Some(self.name_index as u64),
211 resource_type: Some(self.resource_type),
212 ..Default::default()
213 }
214 }
215}
216
217pub struct TaggedClaim(Claim, bool);
219
220#[derive(Debug)]
221pub struct InflatedResource {
222 resource: Resource,
223 claims: HashSet<Claim>,
224}
225
226impl InflatedResource {
227 fn new(resource: Resource) -> InflatedResource {
228 InflatedResource { resource, claims: Default::default() }
229 }
230
231 fn children(&self) -> Vec<u64> {
232 match &self.resource.resource_type {
233 fplugin::ResourceType::Job(job) => {
234 let mut r: Vec<u64> = job.child_jobs.iter().flatten().map(|k| *k).collect();
235 r.extend(job.processes.iter().flatten().map(|k| *k));
236 if r.len() == 0 {
237 eprintln!("{} has no processes", self.resource.koid);
238 }
239 r
240 }
241 fplugin::ResourceType::Process(process) => {
242 process.vmos.iter().flatten().map(|k| *k).collect()
243 }
244 fplugin::ResourceType::Vmo(_) => Vec::new(),
245 _ => todo!(),
246 }
247 }
248
249 fn process_claims(&mut self) {
259 let mut claims_by_source: HashMap<PrincipalIdentifier, RefCell<Vec<TaggedClaim>>> =
260 Default::default();
261 let mut self_claims = Vec::new();
262
263 for claim in self.claims.iter().cloned() {
264 if claim.source == claim.subject {
265 self_claims.push(claim);
268 } else {
269 claims_by_source
270 .entry(claim.source)
271 .or_default()
272 .borrow_mut()
273 .push(TaggedClaim(claim, false));
274 }
275 }
276
277 self.claims = self_claims.into_iter().collect();
278 for (_, claimlist_refcell) in claims_by_source.iter() {
279 let mut claimlist = claimlist_refcell.borrow_mut();
280 for tagged_claim in claimlist.iter_mut() {
281 self.claims.extend(
282 InflatedResource::process_claims_recursive(tagged_claim, &claims_by_source)
283 .into_iter(),
284 );
285 }
286 }
287 }
288
289 fn process_claims_recursive(
291 tagged_claim: &mut TaggedClaim,
292 claims: &HashMap<PrincipalIdentifier, RefCell<Vec<TaggedClaim>>>,
293 ) -> Vec<Claim> {
294 let claim = match tagged_claim.1 {
295 true => {
296 return vec![];
298 }
299 false => {
300 tagged_claim.1 = true;
302 tagged_claim.0
303 }
304 };
305 let subject = &claim.subject;
306 let mut subject_claims = match claims.get(subject) {
308 Some(value_ref) => {
309 value_ref.try_borrow_mut().expect("Claims form a cycle, this is not supported")
314 }
315 None => {
316 return vec![claim];
318 }
319 };
320 let mut leaves = vec![];
321 for subject_claim in subject_claims.iter_mut() {
322 leaves.append(&mut InflatedResource::process_claims_recursive(subject_claim, claims));
323 }
324 leaves
325 }
326}
327
328#[derive(Clone)]
329pub struct Attribution {
331 pub source: PrincipalIdentifier,
333 pub subject: PrincipalIdentifier,
335 pub resources: Vec<ResourceReference>,
337}
338
339impl From<fplugin::Attribution> for Attribution {
340 fn from(value: fplugin::Attribution) -> Attribution {
341 Attribution {
342 source: value.source.unwrap().into(),
343 subject: value.subject.unwrap().into(),
344 resources: value.resources.unwrap().into_iter().map(|r| r.into()).collect(),
345 }
346 }
347}
348
349impl Into<fplugin::Attribution> for Attribution {
350 fn into(self) -> fplugin::Attribution {
351 fplugin::Attribution {
352 source: Some(self.source.into()),
353 subject: Some(self.subject.into()),
354 resources: Some(self.resources.into_iter().map(|r| r.into()).collect()),
355 ..Default::default()
356 }
357 }
358}
359
360#[derive(Clone, Copy)]
361pub enum ResourceReference {
364 KernelObject(u64),
369
370 ProcessMapped {
372 process: u64,
374
375 base: u64,
377
378 len: u64,
380 },
381}
382
383impl From<fplugin::ResourceReference> for ResourceReference {
384 fn from(value: fplugin::ResourceReference) -> ResourceReference {
385 match value {
386 fidl_fuchsia_memory_attribution_plugin::ResourceReference::KernelObject(ko) => {
387 ResourceReference::KernelObject(ko)
388 }
389 fidl_fuchsia_memory_attribution_plugin::ResourceReference::ProcessMapped(
390 fplugin::ProcessMapped { process, base, len },
391 ) => ResourceReference::ProcessMapped { process, base, len },
392 _ => unimplemented!(),
393 }
394 }
395}
396
397impl Into<fplugin::ResourceReference> for ResourceReference {
398 fn into(self) -> fplugin::ResourceReference {
399 match self {
400 ResourceReference::KernelObject(ko) => {
401 fidl_fuchsia_memory_attribution_plugin::ResourceReference::KernelObject(ko)
402 }
403 ResourceReference::ProcessMapped { process, base, len } => {
404 fidl_fuchsia_memory_attribution_plugin::ResourceReference::ProcessMapped(
405 fplugin::ProcessMapped { process, base, len },
406 )
407 }
408 }
409 }
410}
411
412pub struct AttributionData {
415 pub principals_vec: Vec<Principal>,
416 pub resources_vec: Vec<Resource>,
417 pub resource_names: Vec<String>,
418 pub attributions: Vec<Attribution>,
419}
420
421pub trait AttributionDataProvider: Send + Sync {
422 fn get_attribution_data(&self) -> BoxFuture<'_, Result<AttributionData, anyhow::Error>>;
423}
424
425pub struct ProcessedAttributionData {
428 principals: HashMap<PrincipalIdentifier, RefCell<InflatedPrincipal>>,
429 resources: HashMap<u64, RefCell<InflatedResource>>,
430 resource_names: Vec<String>,
431}
432
433impl ProcessedAttributionData {
434 fn new(
435 principals: HashMap<PrincipalIdentifier, RefCell<InflatedPrincipal>>,
436 resources: HashMap<u64, RefCell<InflatedResource>>,
437 resource_names: Vec<String>,
438 ) -> Self {
439 Self { principals, resources, resource_names }
440 }
441
442 pub fn summary(&self) -> MemorySummary {
444 MemorySummary::build(&self.principals, &self.resources, &self.resource_names)
445 }
446}
447
448pub fn attribute_vmos(attribution_data: AttributionData) -> ProcessedAttributionData {
450 let principals: HashMap<PrincipalIdentifier, RefCell<InflatedPrincipal>> = attribution_data
452 .principals_vec
453 .into_iter()
454 .map(|p| (p.identifier.clone(), RefCell::new(InflatedPrincipal::new(p))))
455 .collect();
456
457 let mut resources: HashMap<u64, RefCell<InflatedResource>> = attribution_data
459 .resources_vec
460 .into_iter()
461 .map(|r| (r.koid, RefCell::new(InflatedResource::new(r))))
462 .collect();
463
464 for attribution in attribution_data.attributions {
466 principals.get(&attribution.subject.clone().into()).map(|p| {
467 p.borrow_mut().attribution_claims.insert(attribution.source.into(), attribution.clone())
468 });
469 for resource in attribution.resources {
470 match resource {
471 ResourceReference::KernelObject(koid) => {
472 if !resources.contains_key(&koid) {
473 continue;
474 }
475 resources.get_mut(&koid).unwrap().get_mut().claims.insert(Claim {
476 source: attribution.source.into(),
477 subject: attribution.subject.into(),
478 claim_type: ClaimType::Direct,
479 });
480 }
481 ResourceReference::ProcessMapped { process, base, len } => {
482 if !resources.contains_key(&process) {
483 continue;
484 }
485 let mut matched_vmos = Vec::new();
486 if let fplugin::ResourceType::Process(process_data) =
487 &resources.get(&process).unwrap().borrow().resource.resource_type
488 {
489 for mapping in process_data.mappings.iter().flatten() {
490 if mapping.address_base.unwrap() >= base
493 && mapping.address_base.unwrap() + mapping.size.unwrap()
494 <= base + len
495 {
496 matched_vmos.push(mapping.vmo.unwrap());
497 }
498 }
499 }
500 for vmo_koid in matched_vmos {
501 match resources.get_mut(&vmo_koid) {
502 Some(resource) => {
503 resource.get_mut().claims.insert(Claim {
504 source: attribution.source.into(),
505 subject: attribution.subject.into(),
506 claim_type: ClaimType::Direct,
507 });
508 }
509 None => {
510 }
514 }
515 }
516 }
517 }
518 }
519 }
520
521 for (_, resource_refcell) in &resources {
526 let resource = resource_refcell.borrow_mut();
527 let direct_claims: Vec<&Claim> = resource
529 .claims
530 .iter()
531 .filter(|claim| match claim.claim_type {
532 ClaimType::Direct => true,
533 _ => false,
534 })
535 .collect();
536
537 if direct_claims.is_empty() {
538 continue;
540 }
541
542 let propagated_claims: Vec<Claim> = direct_claims
543 .into_iter()
544 .map(|claim| Claim {
545 source: claim.source,
546 subject: claim.subject,
547 claim_type: ClaimType::Indirect,
548 })
549 .collect();
550 let mut frontier = Vec::new();
551 frontier.extend(resource.children());
552 while !frontier.is_empty() {
553 let child = frontier.pop().unwrap();
554 let mut child_resource = match resources.get(&child) {
555 Some(resource) => resource.borrow_mut(),
556 None => {
557 println!("Resource {} not found", child);
561 continue;
562 }
563 };
564 if child_resource.claims.iter().any(|c| c.claim_type == ClaimType::Direct) {
565 continue;
567 }
568 child_resource.claims.extend(propagated_claims.clone().iter());
569 frontier.extend(child_resource.children().iter());
570 }
571 }
572
573 for (_, resource_refcell) in &resources {
574 let mut resource = resource_refcell.borrow_mut();
575 resource.process_claims();
576 }
577
578 for (resource_id, resource_refcell) in &resources {
581 let resource = resource_refcell.borrow();
582 if let fplugin::ResourceType::Vmo(_) = &resource.resource.resource_type {
583 for claim in &resource.claims {
584 principals.get(&claim.subject).unwrap().borrow_mut().resources.insert(*resource_id);
585 }
586 } else if let fplugin::ResourceType::Process(_) = &resource.resource.resource_type {
587 for claim in &resource.claims {
588 principals
589 .get(&claim.subject)
590 .unwrap()
591 .borrow_mut()
592 .resources
593 .insert(resource.resource.koid);
594 }
595 }
596 }
597
598 ProcessedAttributionData::new(principals, resources, attribution_data.resource_names)
599}
600
601#[cfg(test)]
602mod tests {
603 use std::collections::HashMap;
604
605 use super::*;
606 use fidl_fuchsia_memory_attribution_plugin as fplugin;
607 use summary::{PrincipalSummary, VmoSummary};
608
609 #[test]
610 fn test_gather_resources() {
611 let resource_names = vec![
635 "root_job".to_owned(),
636 "root_process".to_owned(),
637 "root_vmo".to_owned(),
638 "shared_vmo".to_owned(),
639 "runner_job".to_owned(),
640 "runner_process".to_owned(),
641 "runner_vmo".to_owned(),
642 "component_vmo".to_owned(),
643 "component_2_job".to_owned(),
644 "2_process".to_owned(),
645 "2_vmo".to_owned(),
646 "2_vmo_parent".to_owned(),
647 "component_vmo_mapped".to_owned(),
648 "component_vmo_mapped2".to_owned(),
649 ];
650
651 let attributions = vec![
652 fplugin::Attribution {
653 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
654 subject: Some(fplugin::PrincipalIdentifier { id: 0 }),
655 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
656 ..Default::default()
657 },
658 fplugin::Attribution {
659 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
660 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
661 resources: Some(vec![fplugin::ResourceReference::KernelObject(1004)]),
662 ..Default::default()
663 },
664 fplugin::Attribution {
665 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
666 subject: Some(fplugin::PrincipalIdentifier { id: 2 }),
667 resources: Some(vec![fplugin::ResourceReference::KernelObject(1008)]),
668 ..Default::default()
669 },
670 fplugin::Attribution {
671 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
672 subject: Some(fplugin::PrincipalIdentifier { id: 3 }),
673 resources: Some(vec![
674 fplugin::ResourceReference::KernelObject(1007),
675 fplugin::ResourceReference::ProcessMapped(fplugin::ProcessMapped {
676 process: 1005,
677 base: 1024,
678 len: 1024,
679 }),
680 ]),
681 ..Default::default()
682 },
683 ]
684 .into_iter()
685 .map(|a| a.into())
686 .collect();
687
688 let principals = vec![
689 fplugin::Principal {
690 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
691 description: Some(fplugin::Description::Component("root".to_owned())),
692 principal_type: Some(fplugin::PrincipalType::Runnable),
693 parent: None,
694 ..Default::default()
695 },
696 fplugin::Principal {
697 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
698 description: Some(fplugin::Description::Component("runner".to_owned())),
699 principal_type: Some(fplugin::PrincipalType::Runnable),
700 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
701 ..Default::default()
702 },
703 fplugin::Principal {
704 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
705 description: Some(fplugin::Description::Component("component 2".to_owned())),
706 principal_type: Some(fplugin::PrincipalType::Runnable),
707 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
708 ..Default::default()
709 },
710 fplugin::Principal {
711 identifier: Some(fplugin::PrincipalIdentifier { id: 3 }),
712 description: Some(fplugin::Description::Component("component 3".to_owned())),
713 principal_type: Some(fplugin::PrincipalType::Runnable),
714 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
715 ..Default::default()
716 },
717 ]
718 .into_iter()
719 .map(|p| p.into())
720 .collect();
721
722 let resources = vec![
723 fplugin::Resource {
724 koid: Some(1000),
725 name_index: Some(0),
726 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
727 child_jobs: Some(vec![1004, 1008]),
728 processes: Some(vec![1001]),
729 ..Default::default()
730 })),
731 ..Default::default()
732 },
733 fplugin::Resource {
734 koid: Some(1001),
735 name_index: Some(1),
736 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
737 vmos: Some(vec![1002, 1003]),
738 mappings: None,
739 ..Default::default()
740 })),
741 ..Default::default()
742 },
743 fplugin::Resource {
744 koid: Some(1002),
745 name_index: Some(2),
746 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
747 private_committed_bytes: Some(1024),
748 private_populated_bytes: Some(2048),
749 scaled_committed_bytes: Some(1024),
750 scaled_populated_bytes: Some(2048),
751 total_committed_bytes: Some(1024),
752 total_populated_bytes: Some(2048),
753 ..Default::default()
754 })),
755 ..Default::default()
756 },
757 fplugin::Resource {
758 koid: Some(1003),
759 name_index: Some(3),
760 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
761 private_committed_bytes: Some(1024),
762 private_populated_bytes: Some(2048),
763 scaled_committed_bytes: Some(1024),
764 scaled_populated_bytes: Some(2048),
765 total_committed_bytes: Some(1024),
766 total_populated_bytes: Some(2048),
767 ..Default::default()
768 })),
769 ..Default::default()
770 },
771 fplugin::Resource {
772 koid: Some(1004),
773 name_index: Some(4),
774 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
775 child_jobs: Some(vec![]),
776 processes: Some(vec![1005]),
777 ..Default::default()
778 })),
779 ..Default::default()
780 },
781 fplugin::Resource {
782 koid: Some(1005),
783 name_index: Some(5),
784 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
785 vmos: Some(vec![1006, 1007, 1012]),
786 mappings: Some(vec![
787 fplugin::Mapping {
788 vmo: Some(1006),
789 address_base: Some(0),
790 size: Some(512),
791 ..Default::default()
792 },
793 fplugin::Mapping {
794 vmo: Some(1012),
795 address_base: Some(1024),
796 size: Some(512),
797 ..Default::default()
798 },
799 fplugin::Mapping {
800 vmo: Some(1013),
801 address_base: Some(1536),
802 size: Some(512),
803 ..Default::default()
804 },
805 fplugin::Mapping {
806 vmo: Some(1006),
807 address_base: Some(2048),
808 size: Some(512),
809 ..Default::default()
810 },
811 ]),
812 ..Default::default()
813 })),
814 ..Default::default()
815 },
816 fplugin::Resource {
817 koid: Some(1006),
818 name_index: Some(6),
819 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
820 private_committed_bytes: Some(1024),
821 private_populated_bytes: Some(2048),
822 scaled_committed_bytes: Some(1024),
823 scaled_populated_bytes: Some(2048),
824 total_committed_bytes: Some(1024),
825 total_populated_bytes: Some(2048),
826 ..Default::default()
827 })),
828 ..Default::default()
829 },
830 fplugin::Resource {
831 koid: Some(1007),
832 name_index: Some(7),
833 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
834 private_committed_bytes: Some(128),
835 private_populated_bytes: Some(256),
836 scaled_committed_bytes: Some(128),
837 scaled_populated_bytes: Some(256),
838 total_committed_bytes: Some(128),
839 total_populated_bytes: Some(256),
840 ..Default::default()
841 })),
842 ..Default::default()
843 },
844 fplugin::Resource {
845 koid: Some(1008),
846 name_index: Some(8),
847 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
848 child_jobs: Some(vec![]),
849 processes: Some(vec![1009]),
850 ..Default::default()
851 })),
852 ..Default::default()
853 },
854 fplugin::Resource {
855 koid: Some(1009),
856 name_index: Some(9),
857 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
858 vmos: Some(vec![1010, 1003]),
859 mappings: None,
860 ..Default::default()
861 })),
862 ..Default::default()
863 },
864 fplugin::Resource {
865 koid: Some(1010),
866 name_index: Some(10),
867 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
868 parent: Some(1011),
869 private_committed_bytes: Some(1024),
870 private_populated_bytes: Some(2048),
871 scaled_committed_bytes: Some(1024),
872 scaled_populated_bytes: Some(2048),
873 total_committed_bytes: Some(1024),
874 total_populated_bytes: Some(2048),
875 ..Default::default()
876 })),
877 ..Default::default()
878 },
879 fplugin::Resource {
880 koid: Some(1011),
881 name_index: Some(11),
882 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
883 private_committed_bytes: Some(1024),
884 private_populated_bytes: Some(2048),
885 scaled_committed_bytes: Some(1024),
886 scaled_populated_bytes: Some(2048),
887 total_committed_bytes: Some(1024),
888 total_populated_bytes: Some(2048),
889 ..Default::default()
890 })),
891 ..Default::default()
892 },
893 fplugin::Resource {
894 koid: Some(1012),
895 name_index: Some(12),
896 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
897 private_committed_bytes: Some(1024),
898 private_populated_bytes: Some(2048),
899 scaled_committed_bytes: Some(1024),
900 scaled_populated_bytes: Some(2048),
901 total_committed_bytes: Some(1024),
902 total_populated_bytes: Some(2048),
903 ..Default::default()
904 })),
905 ..Default::default()
906 },
907 fplugin::Resource {
908 koid: Some(1013),
909 name_index: Some(13),
910 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
911 private_committed_bytes: Some(1024),
912 private_populated_bytes: Some(2048),
913 scaled_committed_bytes: Some(1024),
914 scaled_populated_bytes: Some(2048),
915 total_committed_bytes: Some(1024),
916 total_populated_bytes: Some(2048),
917 ..Default::default()
918 })),
919 ..Default::default()
920 },
921 ]
922 .into_iter()
923 .map(|r| r.into())
924 .collect();
925
926 let output = attribute_vmos(AttributionData {
927 principals_vec: principals,
928 resources_vec: resources,
929 resource_names,
930 attributions,
931 })
932 .summary();
933
934 assert_eq!(output.undigested, 2048);
935 assert_eq!(output.principals.len(), 4);
936
937 let principals: HashMap<u64, PrincipalSummary> =
938 output.principals.into_iter().map(|p| (p.id, p)).collect();
939
940 assert_eq!(
941 principals.get(&0).unwrap(),
942 &PrincipalSummary {
943 id: 0,
944 name: "root".to_owned(),
945 principal_type: "R".to_owned(),
946 committed_private: 1024,
947 committed_scaled: 1536.0,
948 committed_total: 2048,
949 populated_private: 2048,
950 populated_scaled: 3072.0,
951 populated_total: 4096,
952 attributor: None,
953 processes: vec!["root_process (1001)".to_owned()],
954 vmos: vec![
955 (
956 "root_vmo".to_owned(),
957 VmoSummary {
958 count: 1,
959 committed_private: 1024,
960 committed_scaled: 1024.0,
961 committed_total: 1024,
962 populated_private: 2048,
963 populated_scaled: 2048.0,
964 populated_total: 2048,
965 ..Default::default()
966 }
967 ),
968 (
969 "shared_vmo".to_owned(),
970 VmoSummary {
971 count: 1,
972 committed_private: 0,
973 committed_scaled: 512.0,
974 committed_total: 1024,
975 populated_private: 0,
976 populated_scaled: 1024.0,
977 populated_total: 2048,
978 ..Default::default()
979 }
980 )
981 ]
982 .into_iter()
983 .collect(),
984 }
985 );
986
987 assert_eq!(
988 principals.get(&1).unwrap(),
989 &PrincipalSummary {
990 id: 1,
991 name: "runner".to_owned(),
992 principal_type: "R".to_owned(),
993 committed_private: 1024,
994 committed_scaled: 1024.0,
995 committed_total: 1024,
996 populated_private: 2048,
997 populated_scaled: 2048.0,
998 populated_total: 2048,
999 attributor: Some("root".to_owned()),
1000 processes: vec!["runner_process (1005)".to_owned()],
1001 vmos: vec![(
1002 "runner_vmo".to_owned(),
1003 VmoSummary {
1004 count: 1,
1005 committed_private: 1024,
1006 committed_scaled: 1024.0,
1007 committed_total: 1024,
1008 populated_private: 2048,
1009 populated_scaled: 2048.0,
1010 populated_total: 2048,
1011 ..Default::default()
1012 }
1013 )]
1014 .into_iter()
1015 .collect(),
1016 }
1017 );
1018
1019 assert_eq!(
1020 principals.get(&2).unwrap(),
1021 &PrincipalSummary {
1022 id: 2,
1023 name: "component 2".to_owned(),
1024 principal_type: "R".to_owned(),
1025 committed_private: 1024,
1026 committed_scaled: 1536.0,
1027 committed_total: 2048,
1028 populated_private: 2048,
1029 populated_scaled: 3072.0,
1030 populated_total: 4096,
1031 attributor: Some("root".to_owned()),
1032 processes: vec!["2_process (1009)".to_owned()],
1033 vmos: vec![
1034 (
1035 "shared_vmo".to_owned(),
1036 VmoSummary {
1037 count: 1,
1038 committed_private: 0,
1039 committed_scaled: 512.0,
1040 committed_total: 1024,
1041 populated_private: 0,
1042 populated_scaled: 1024.0,
1043 populated_total: 2048,
1044 ..Default::default()
1045 }
1046 ),
1047 (
1048 "2_vmo".to_owned(),
1049 VmoSummary {
1050 count: 1,
1051 committed_private: 1024,
1052 committed_scaled: 1024.0,
1053 committed_total: 1024,
1054 populated_private: 2048,
1055 populated_scaled: 2048.0,
1056 populated_total: 2048,
1057 ..Default::default()
1058 }
1059 )
1060 ]
1061 .into_iter()
1062 .collect(),
1063 }
1064 );
1065
1066 assert_eq!(
1067 principals.get(&3).unwrap(),
1068 &PrincipalSummary {
1069 id: 3,
1070 name: "component 3".to_owned(),
1071 principal_type: "R".to_owned(),
1072 committed_private: 2176,
1073 committed_scaled: 2176.0,
1074 committed_total: 2176,
1075 populated_private: 4352,
1076 populated_scaled: 4352.0,
1077 populated_total: 4352,
1078 attributor: Some("runner".to_owned()),
1079 processes: vec!["runner_process (1005)".to_owned()],
1080 vmos: vec![
1081 (
1082 "component_vmo".to_owned(),
1083 VmoSummary {
1084 count: 1,
1085 committed_private: 128,
1086 committed_scaled: 128.0,
1087 committed_total: 128,
1088 populated_private: 256,
1089 populated_scaled: 256.0,
1090 populated_total: 256,
1091 ..Default::default()
1092 }
1093 ),
1094 (
1095 "component_vmo_mapped".to_owned(),
1096 VmoSummary {
1097 count: 1,
1098 committed_private: 1024,
1099 committed_scaled: 1024.0,
1100 committed_total: 1024,
1101 populated_private: 2048,
1102 populated_scaled: 2048.0,
1103 populated_total: 2048,
1104 ..Default::default()
1105 }
1106 ),
1107 (
1108 "component_vmo_mapped2".to_owned(),
1109 VmoSummary {
1110 count: 1,
1111 committed_private: 1024,
1112 committed_scaled: 1024.0,
1113 committed_total: 1024,
1114 populated_private: 2048,
1115 populated_scaled: 2048.0,
1116 populated_total: 2048,
1117 ..Default::default()
1118 }
1119 )
1120 ]
1121 .into_iter()
1122 .collect(),
1123 }
1124 );
1125 }
1126
1127 #[test]
1128 fn test_reshare_resources() {
1129 let resource_names = vec![
1143 "root_job".to_owned(),
1144 "component_job".to_owned(),
1145 "component_process".to_owned(),
1146 "component_vmo".to_owned(),
1147 ];
1148 let attributions = vec![
1149 fplugin::Attribution {
1150 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1151 subject: Some(fplugin::PrincipalIdentifier { id: 0 }),
1152 resources: Some(vec![fplugin::ResourceReference::KernelObject(1000)]),
1153 ..Default::default()
1154 },
1155 fplugin::Attribution {
1156 source: Some(fplugin::PrincipalIdentifier { id: 0 }),
1157 subject: Some(fplugin::PrincipalIdentifier { id: 1 }),
1158 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1159 ..Default::default()
1160 },
1161 fplugin::Attribution {
1162 source: Some(fplugin::PrincipalIdentifier { id: 1 }),
1163 subject: Some(fplugin::PrincipalIdentifier { id: 2 }),
1164 resources: Some(vec![fplugin::ResourceReference::KernelObject(1001)]),
1165 ..Default::default()
1166 },
1167 ]
1168 .into_iter()
1169 .map(|a| a.into())
1170 .collect();
1171 let principals = vec![
1172 fplugin::Principal {
1173 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
1174 description: Some(fplugin::Description::Component("root".to_owned())),
1175 principal_type: Some(fplugin::PrincipalType::Runnable),
1176 parent: None,
1177 ..Default::default()
1178 },
1179 fplugin::Principal {
1180 identifier: Some(fplugin::PrincipalIdentifier { id: 1 }),
1181 description: Some(fplugin::Description::Component("component 1".to_owned())),
1182 principal_type: Some(fplugin::PrincipalType::Runnable),
1183 parent: Some(fplugin::PrincipalIdentifier { id: 0 }),
1184 ..Default::default()
1185 },
1186 fplugin::Principal {
1187 identifier: Some(fplugin::PrincipalIdentifier { id: 2 }),
1188 description: Some(fplugin::Description::Component("component 2".to_owned())),
1189 principal_type: Some(fplugin::PrincipalType::Runnable),
1190 parent: Some(fplugin::PrincipalIdentifier { id: 1 }),
1191 ..Default::default()
1192 },
1193 ]
1194 .into_iter()
1195 .map(|p| p.into())
1196 .collect();
1197
1198 let resources = vec![
1199 fplugin::Resource {
1200 koid: Some(1000),
1201 name_index: Some(0),
1202 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1203 child_jobs: Some(vec![1001]),
1204 processes: Some(vec![]),
1205 ..Default::default()
1206 })),
1207 ..Default::default()
1208 },
1209 fplugin::Resource {
1210 koid: Some(1001),
1211 name_index: Some(1),
1212 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1213 child_jobs: Some(vec![]),
1214 processes: Some(vec![1002]),
1215 ..Default::default()
1216 })),
1217 ..Default::default()
1218 },
1219 fplugin::Resource {
1220 koid: Some(1002),
1221 name_index: Some(2),
1222 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1223 vmos: Some(vec![1003]),
1224 mappings: None,
1225 ..Default::default()
1226 })),
1227 ..Default::default()
1228 },
1229 fplugin::Resource {
1230 koid: Some(1003),
1231 name_index: Some(3),
1232 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1233 private_committed_bytes: Some(1024),
1234 private_populated_bytes: Some(2048),
1235 scaled_committed_bytes: Some(1024),
1236 scaled_populated_bytes: Some(2048),
1237 total_committed_bytes: Some(1024),
1238 total_populated_bytes: Some(2048),
1239 ..Default::default()
1240 })),
1241 ..Default::default()
1242 },
1243 ]
1244 .into_iter()
1245 .map(|r| r.into())
1246 .collect();
1247
1248 let output = attribute_vmos(AttributionData {
1249 principals_vec: principals,
1250 resources_vec: resources,
1251 resource_names,
1252 attributions,
1253 })
1254 .summary();
1255
1256 assert_eq!(output.undigested, 0);
1257 assert_eq!(output.principals.len(), 3);
1258
1259 let principals: HashMap<u64, PrincipalSummary> =
1260 output.principals.into_iter().map(|p| (p.id, p)).collect();
1261
1262 assert_eq!(
1263 principals.get(&0).unwrap(),
1264 &PrincipalSummary {
1265 id: 0,
1266 name: "root".to_owned(),
1267 principal_type: "R".to_owned(),
1268 committed_private: 0,
1269 committed_scaled: 0.0,
1270 committed_total: 0,
1271 populated_private: 0,
1272 populated_scaled: 0.0,
1273 populated_total: 0,
1274 attributor: None,
1275 processes: vec![],
1276 vmos: vec![].into_iter().collect(),
1277 }
1278 );
1279
1280 assert_eq!(
1281 principals.get(&1).unwrap(),
1282 &PrincipalSummary {
1283 id: 1,
1284 name: "component 1".to_owned(),
1285 principal_type: "R".to_owned(),
1286 committed_private: 0,
1287 committed_scaled: 0.0,
1288 committed_total: 0,
1289 populated_private: 0,
1290 populated_scaled: 0.0,
1291 populated_total: 0,
1292 attributor: Some("root".to_owned()),
1293 processes: vec![],
1294 vmos: vec![].into_iter().collect(),
1295 }
1296 );
1297
1298 assert_eq!(
1299 principals.get(&2).unwrap(),
1300 &PrincipalSummary {
1301 id: 2,
1302 name: "component 2".to_owned(),
1303 principal_type: "R".to_owned(),
1304 committed_private: 1024,
1305 committed_scaled: 1024.0,
1306 committed_total: 1024,
1307 populated_private: 2048,
1308 populated_scaled: 2048.0,
1309 populated_total: 2048,
1310 attributor: Some("component 1".to_owned()),
1311 processes: vec!["component_process (1002)".to_owned()],
1312 vmos: vec![(
1313 "component_vmo".to_owned(),
1314 VmoSummary {
1315 count: 1,
1316 committed_private: 1024,
1317 committed_scaled: 1024.0,
1318 committed_total: 1024,
1319 populated_private: 2048,
1320 populated_scaled: 2048.0,
1321 populated_total: 2048,
1322 ..Default::default()
1323 }
1324 ),]
1325 .into_iter()
1326 .collect(),
1327 }
1328 );
1329 }
1330
1331 #[test]
1332 fn test_conversions() {
1333 let plugin_principal = fplugin::Principal {
1334 identifier: Some(fplugin::PrincipalIdentifier { id: 0 }),
1335 description: Some(fplugin::Description::Component("root".to_owned())),
1336 principal_type: Some(fplugin::PrincipalType::Runnable),
1337 parent: None,
1338 ..Default::default()
1339 };
1340
1341 let data_principal: Principal = plugin_principal.clone().into();
1342
1343 assert_eq!(plugin_principal, data_principal.into());
1344
1345 let plugin_resources = vec![
1346 fplugin::Resource {
1347 koid: Some(1000),
1348 name_index: Some(0),
1349 resource_type: Some(fplugin::ResourceType::Job(fplugin::Job {
1350 child_jobs: Some(vec![1004, 1008]),
1351 processes: Some(vec![1001]),
1352 ..Default::default()
1353 })),
1354 ..Default::default()
1355 },
1356 fplugin::Resource {
1357 koid: Some(1001),
1358 name_index: Some(1),
1359 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1360 vmos: Some(vec![1002, 1003]),
1361 mappings: None,
1362 ..Default::default()
1363 })),
1364 ..Default::default()
1365 },
1366 fplugin::Resource {
1367 koid: Some(1002),
1368 name_index: Some(2),
1369 resource_type: Some(fplugin::ResourceType::Vmo(fplugin::Vmo {
1370 private_committed_bytes: Some(1024),
1371 private_populated_bytes: Some(2048),
1372 scaled_committed_bytes: Some(1024),
1373 scaled_populated_bytes: Some(2048),
1374 total_committed_bytes: Some(1024),
1375 total_populated_bytes: Some(2048),
1376 ..Default::default()
1377 })),
1378 ..Default::default()
1379 },
1380 fplugin::Resource {
1381 koid: Some(1005),
1382 name_index: Some(5),
1383 resource_type: Some(fplugin::ResourceType::Process(fplugin::Process {
1384 vmos: Some(vec![1006, 1007, 1012]),
1385 mappings: Some(vec![
1386 fplugin::Mapping {
1387 vmo: Some(1006),
1388 address_base: Some(0),
1389 size: Some(512),
1390 ..Default::default()
1391 },
1392 fplugin::Mapping {
1393 vmo: Some(1012),
1394 address_base: Some(1024),
1395 size: Some(512),
1396 ..Default::default()
1397 },
1398 ]),
1399 ..Default::default()
1400 })),
1401 ..Default::default()
1402 },
1403 ];
1404
1405 let data_resources: Vec<Resource> =
1406 plugin_resources.iter().cloned().map(|r| r.into()).collect();
1407
1408 let actual_resources: Vec<fplugin::Resource> =
1409 data_resources.into_iter().map(|r| r.into()).collect();
1410
1411 assert_eq!(plugin_resources, actual_resources);
1412 }
1413}