• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #![cfg(feature = "log_page_fault")]
6 #![deny(missing_docs)]
7 
8 use std::fs::File;
9 use std::fs::OpenOptions;
10 use std::io::Write;
11 use std::path::Path;
12 use std::time::Instant;
13 use std::time::SystemTime;
14 use std::time::UNIX_EPOCH;
15 
16 use anyhow::Context;
17 use base::info;
18 use base::AsRawDescriptor;
19 use base::RawDescriptor;
20 use serde::Deserialize;
21 use serde::Serialize;
22 use vm_memory::GuestMemory;
23 use vm_memory::MemoryRegionInformation;
24 
25 /// Logs page fault events into a log file in json format.
26 pub struct PageFaultEventLogger {
27     file: File,
28     base_time: Instant,
29 }
30 
31 impl PageFaultEventLogger {
32     /// Creates a log file named "page_fault.log" in the `swap_dir` and logs the initial log.
33     ///
34     /// initial log contains the regions information and base time.
35     ///
36     /// # Arguments
37     ///
38     /// * `swap_dir` - directory to store the log file.
39     /// * `guest_memory` - [GuestMemory] containing regions info.
create(swap_dir: &Path, guest_memory: &GuestMemory) -> anyhow::Result<Self>40     pub fn create(swap_dir: &Path, guest_memory: &GuestMemory) -> anyhow::Result<Self> {
41         let file_path = swap_dir.join("page_fault.log");
42         let file = OpenOptions::new()
43             .read(true)
44             .write(true)
45             .create(true)
46             .truncate(true)
47             .open(&file_path)
48             .context("open pagefault event log file")?;
49         let regions = regions_from_guest_memory(guest_memory);
50         let base_time = Instant::now();
51         serde_json::to_writer(
52             &file,
53             &PageFaultInitialLog {
54                 base_timestamp: SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis(),
55                 regions,
56             },
57         )
58         .context("log initial event")?;
59         let mut logger = Self { file, base_time };
60         logger.line_break();
61         info!("start logging page faults at {:?}", file_path);
62         Ok(logger)
63     }
64 
65     /// Logs a page fault event.
66     ///
67     /// # Arguments
68     ///
69     /// * `address` - the address page fault occured.
log_page_fault(&mut self, address: usize, id_uffd: u32)70     pub fn log_page_fault(&mut self, address: usize, id_uffd: u32) {
71         // it is not optimized (e.g. buffered io). but it is fine because this logger is for debug
72         // purpose only.
73         let _ = serde_json::to_writer(
74             &self.file,
75             &PageFaultEventLog {
76                 elapsed_nanos: self.base_time.elapsed().as_nanos(),
77                 address,
78                 id_uffd,
79             },
80         );
81         self.line_break();
82     }
83 
line_break(&mut self)84     fn line_break(&mut self) {
85         const LINE_BREAK: &[u8] = &[b'\n'];
86         let _ = self.file.write(LINE_BREAK);
87     }
88 }
89 
90 impl AsRawDescriptor for PageFaultEventLogger {
as_raw_descriptor(&self) -> RawDescriptor91     fn as_raw_descriptor(&self) -> RawDescriptor {
92         self.file.as_raw_descriptor()
93     }
94 }
95 
96 #[derive(Serialize, Deserialize, Debug)]
97 struct PageFaultInitialLog {
98     base_timestamp: u128,
99     regions: Vec<MemoryRegion>,
100 }
101 
regions_from_guest_memory(guest_memory: &GuestMemory) -> Vec<MemoryRegion>102 fn regions_from_guest_memory(guest_memory: &GuestMemory) -> Vec<MemoryRegion> {
103     let mut regions = Vec::new();
104     guest_memory
105         .with_regions::<_, ()>(
106             |MemoryRegionInformation {
107                  len, base_address, ..
108              }| {
109                 regions.push(MemoryRegion { base_address, len });
110                 Ok(())
111             },
112         )
113         .unwrap(); // the call back never return error.
114     regions
115 }
116 
117 #[derive(Serialize, Deserialize, Debug)]
118 struct MemoryRegion {
119     base_address: usize,
120     len: usize,
121 }
122 
123 #[derive(Serialize, Deserialize, Debug)]
124 struct PageFaultEventLog {
125     elapsed_nanos: u128,
126     address: usize,
127     id_uffd: u32,
128 }
129