• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2021 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 //! ProfCollect configurations.
18 
19 use anyhow::Result;
20 use lazy_static::lazy_static;
21 use macaddr::MacAddr6;
22 use rand::Rng;
23 use serde::{Deserialize, Serialize};
24 use std::error::Error;
25 use std::fs::{read_dir, remove_file};
26 use std::path::Path;
27 use std::str::FromStr;
28 use std::time::Duration;
29 
30 const PROFCOLLECT_CONFIG_NAMESPACE: &str = "profcollect_native_boot";
31 const PROFCOLLECT_NODE_ID_PROPERTY: &str = "persist.profcollectd.node_id";
32 
33 const DEFAULT_BINARY_FILTER: &str = "^/(system|apex/.+)/(bin|lib|lib64)/.+";
34 pub const REPORT_RETENTION_SECS: u64 = 14 * 24 * 60 * 60; // 14 days.
35 
36 // Static configs that cannot be changed.
37 lazy_static! {
38     pub static ref TRACE_OUTPUT_DIR: &'static Path = Path::new("/data/misc/profcollectd/trace/");
39     pub static ref PROFILE_OUTPUT_DIR: &'static Path = Path::new("/data/misc/profcollectd/output/");
40     pub static ref REPORT_OUTPUT_DIR: &'static Path = Path::new("/data/misc/profcollectd/report/");
41     pub static ref CONFIG_FILE: &'static Path =
42         Path::new("/data/misc/profcollectd/output/config.json");
43 }
44 
45 /// Dynamic configs, stored in config.json.
46 #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
47 pub struct Config {
48     /// Version of config file scheme, always equals to 1.
49     version: u32,
50     /// Application specific node ID.
51     pub node_id: MacAddr6,
52     /// Device build fingerprint.
53     pub build_fingerprint: String,
54     /// Interval between collections.
55     pub collection_interval: Duration,
56     /// Length of time each collection lasts for.
57     pub sampling_period: Duration,
58     /// An optional filter to limit which binaries to or not to profile.
59     pub binary_filter: String,
60     /// Maximum size of the trace directory.
61     pub max_trace_limit: u64,
62 }
63 
64 impl Config {
from_env() -> Result<Self>65     pub fn from_env() -> Result<Self> {
66         Ok(Config {
67             version: 1,
68             node_id: get_or_initialise_node_id()?,
69             build_fingerprint: get_build_fingerprint()?,
70             collection_interval: Duration::from_secs(get_device_config(
71                 "collection_interval",
72                 600,
73             )?),
74             sampling_period: Duration::from_millis(get_device_config("sampling_period", 500)?),
75             binary_filter: get_device_config("binary_filter", DEFAULT_BINARY_FILTER.to_string())?,
76             max_trace_limit: get_device_config(
77                 "max_trace_limit",
78                 /* 512MB */ 512 * 1024 * 1024,
79             )?,
80         })
81     }
82 }
83 
84 impl ToString for Config {
to_string(&self) -> String85     fn to_string(&self) -> String {
86         serde_json::to_string(self).expect("Failed to deserialise configuration.")
87     }
88 }
89 
90 impl FromStr for Config {
91     type Err = serde_json::Error;
from_str(s: &str) -> Result<Self, Self::Err>92     fn from_str(s: &str) -> Result<Self, Self::Err> {
93         serde_json::from_str::<Config>(s)
94     }
95 }
96 
get_or_initialise_node_id() -> Result<MacAddr6>97 fn get_or_initialise_node_id() -> Result<MacAddr6> {
98     let mut node_id = get_property(PROFCOLLECT_NODE_ID_PROPERTY, MacAddr6::nil())?;
99     if node_id.is_nil() {
100         node_id = generate_random_node_id();
101         set_property(PROFCOLLECT_NODE_ID_PROPERTY, node_id)?;
102     }
103 
104     Ok(node_id)
105 }
106 
get_build_fingerprint() -> Result<String>107 fn get_build_fingerprint() -> Result<String> {
108     get_property("ro.build.fingerprint", "unknown".to_string())
109 }
110 
get_device_config<T>(key: &str, default_value: T) -> Result<T> where T: FromStr + ToString, T::Err: Error + Send + Sync + 'static,111 fn get_device_config<T>(key: &str, default_value: T) -> Result<T>
112 where
113     T: FromStr + ToString,
114     T::Err: Error + Send + Sync + 'static,
115 {
116     let default_value = default_value.to_string();
117     let config = profcollect_libflags_rust::GetServerConfigurableFlag(
118         PROFCOLLECT_CONFIG_NAMESPACE,
119         key,
120         &default_value,
121     );
122     Ok(T::from_str(&config)?)
123 }
124 
get_property<T>(key: &str, default_value: T) -> Result<T> where T: FromStr + ToString, T::Err: Error + Send + Sync + 'static,125 fn get_property<T>(key: &str, default_value: T) -> Result<T>
126 where
127     T: FromStr + ToString,
128     T::Err: Error + Send + Sync + 'static,
129 {
130     let default_value = default_value.to_string();
131     let value = rustutils::system_properties::read(key).unwrap_or(None).unwrap_or(default_value);
132     Ok(T::from_str(&value)?)
133 }
134 
set_property<T>(key: &str, value: T) -> Result<()> where T: ToString,135 fn set_property<T>(key: &str, value: T) -> Result<()>
136 where
137     T: ToString,
138 {
139     let value = value.to_string();
140     Ok(rustutils::system_properties::write(key, &value)?)
141 }
142 
generate_random_node_id() -> MacAddr6143 fn generate_random_node_id() -> MacAddr6 {
144     let mut node_id = rand::thread_rng().gen::<[u8; 6]>();
145     node_id[0] |= 0x1;
146     MacAddr6::from(node_id)
147 }
148 
clear_data() -> Result<()>149 pub fn clear_data() -> Result<()> {
150     fn remove_files(path: &Path) -> Result<()> {
151         read_dir(path)?
152             .filter_map(|e| e.ok())
153             .map(|e| e.path())
154             .filter(|e| e.is_file())
155             .try_for_each(remove_file)?;
156         Ok(())
157     }
158 
159     remove_files(&TRACE_OUTPUT_DIR)?;
160     remove_files(&PROFILE_OUTPUT_DIR)?;
161     remove_files(&REPORT_OUTPUT_DIR)?;
162     Ok(())
163 }
164