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 pub const REPORT_RETENTION_SECS: u64 = 14 * 24 * 60 * 60; // 14 days.
34
35 // Static configs that cannot be changed.
36 lazy_static! {
37 pub static ref TRACE_OUTPUT_DIR: &'static Path = Path::new("/data/misc/profcollectd/trace/");
38 pub static ref PROFILE_OUTPUT_DIR: &'static Path = Path::new("/data/misc/profcollectd/output/");
39 pub static ref REPORT_OUTPUT_DIR: &'static Path = Path::new("/data/misc/profcollectd/report/");
40 pub static ref BETTERBUG_CACHE_DIR_PREFIX: &'static Path = Path::new("/data/user/");
41 pub static ref BETTERBUG_CACHE_DIR_SUFFIX: &'static Path =
42 Path::new("com.google.android.apps.internal.betterbug/cache/");
43 pub static ref CONFIG_FILE: &'static Path =
44 Path::new("/data/misc/profcollectd/output/config.json");
45 }
46
47 /// Dynamic configs, stored in config.json.
48 #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
49 pub struct Config {
50 /// Version of config file scheme, always equals to 1.
51 version: u32,
52 /// Application specific node ID.
53 pub node_id: MacAddr6,
54 /// Device build fingerprint.
55 pub build_fingerprint: String,
56 /// Interval between collections.
57 pub collection_interval: Duration,
58 /// Length of time each collection lasts for.
59 pub sampling_period: Duration,
60 /// An optional filter to limit which binaries to or not to profile.
61 pub binary_filter: String,
62 /// Maximum size of the trace directory.
63 pub max_trace_limit: u64,
64 }
65
66 impl Config {
from_env() -> Result<Self>67 pub fn from_env() -> Result<Self> {
68 Ok(Config {
69 version: 1,
70 node_id: get_or_initialise_node_id()?,
71 build_fingerprint: get_build_fingerprint()?,
72 collection_interval: Duration::from_secs(get_device_config(
73 "collection_interval",
74 600,
75 )?),
76 sampling_period: Duration::from_millis(get_device_config("sampling_period", 500)?),
77 binary_filter: get_device_config("binary_filter", "".to_string())?,
78 max_trace_limit: get_device_config(
79 "max_trace_limit",
80 /* 512MB */ 512 * 1024 * 1024,
81 )?,
82 })
83 }
84 }
85
86 impl ToString for Config {
to_string(&self) -> String87 fn to_string(&self) -> String {
88 serde_json::to_string(self).expect("Failed to deserialise configuration.")
89 }
90 }
91
92 impl FromStr for Config {
93 type Err = serde_json::Error;
from_str(s: &str) -> Result<Self, Self::Err>94 fn from_str(s: &str) -> Result<Self, Self::Err> {
95 serde_json::from_str::<Config>(s)
96 }
97 }
98
get_or_initialise_node_id() -> Result<MacAddr6>99 fn get_or_initialise_node_id() -> Result<MacAddr6> {
100 let mut node_id = get_property(&PROFCOLLECT_NODE_ID_PROPERTY, MacAddr6::nil())?;
101 if node_id.is_nil() {
102 node_id = generate_random_node_id();
103 set_property(&PROFCOLLECT_NODE_ID_PROPERTY, node_id);
104 }
105
106 Ok(node_id)
107 }
108
get_build_fingerprint() -> Result<String>109 fn get_build_fingerprint() -> Result<String> {
110 get_property("ro.build.fingerprint", "unknown".to_string())
111 }
112
get_device_config<T>(key: &str, default_value: T) -> Result<T> where T: FromStr + ToString, T::Err: Error + Send + Sync + 'static,113 fn get_device_config<T>(key: &str, default_value: T) -> Result<T>
114 where
115 T: FromStr + ToString,
116 T::Err: Error + Send + Sync + 'static,
117 {
118 let default_value = default_value.to_string();
119 let config = profcollect_libflags_rust::GetServerConfigurableFlag(
120 &PROFCOLLECT_CONFIG_NAMESPACE,
121 &key,
122 &default_value,
123 );
124 Ok(T::from_str(&config)?)
125 }
126
get_property<T>(key: &str, default_value: T) -> Result<T> where T: FromStr + ToString, T::Err: Error + Send + Sync + 'static,127 fn get_property<T>(key: &str, default_value: T) -> Result<T>
128 where
129 T: FromStr + ToString,
130 T::Err: Error + Send + Sync + 'static,
131 {
132 let default_value = default_value.to_string();
133 let value = profcollect_libbase_rust::GetProperty(&key, &default_value);
134 Ok(T::from_str(&value)?)
135 }
136
set_property<T>(key: &str, value: T) where T: ToString,137 fn set_property<T>(key: &str, value: T)
138 where
139 T: ToString,
140 {
141 let value = value.to_string();
142 profcollect_libbase_rust::SetProperty(&key, &value);
143 }
144
generate_random_node_id() -> MacAddr6145 fn generate_random_node_id() -> MacAddr6 {
146 let mut node_id = rand::thread_rng().gen::<[u8; 6]>();
147 node_id[0] |= 0x1;
148 MacAddr6::from(node_id)
149 }
150
clear_data() -> Result<()>151 pub fn clear_data() -> Result<()> {
152 fn remove_files(path: &Path) -> Result<()> {
153 read_dir(path)?
154 .filter_map(|e| e.ok())
155 .map(|e| e.path())
156 .filter(|e| e.is_file())
157 .try_for_each(remove_file)?;
158 Ok(())
159 }
160
161 remove_files(&TRACE_OUTPUT_DIR)?;
162 remove_files(&PROFILE_OUTPUT_DIR)?;
163 remove_files(&REPORT_OUTPUT_DIR)?;
164 Ok(())
165 }
166