sl4f_lib/traceutil/facade.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::Error;
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
use base64::engine::Engine as _;
use serde_json::{to_value, Value};
use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
/// Perform Traceutil operations.
///
/// Note this object is shared among all threads created by server.
///
/// This facade does not hold onto a Traceutil proxy as the server may be
/// long-running while individual tests set up and tear down Traceutil.
///
/// WARNING: Use of this facade is discouraged as its functionality is only to download traces that
/// were collected through other means (such as running the trace binary over ssh). Instead, see
/// TracingFacade, which allows for control of the tracing system as well.
#[derive(Debug)]
pub struct TraceutilFacade {}
// Chunk size of 8 MiB. Because we read a full chunk into memory and convert it
// to base64, in cases where the system is close to OOM, a small chunk size
// makes it more likely that we can successfully download the trace. On the other
// hand, a very small chunk size slows down the trace download. Empirically, 8 MiB
// seems to be a reasonable compromise.
const MAX_CHUNK_SIZE: usize = 8 * 1024 * 1024;
impl TraceutilFacade {
pub fn new() -> TraceutilFacade {
TraceutilFacade {}
}
/// Gets data from the specified path starting from an optional offset.
///
/// Loading and returning the entire file is problematic for large trace files,
/// so will return up to |MAX_CHUNK_SIZE| bytes at once. The bytes of the file are
/// returned in a field called |data|. If there is more data to read, then a field
/// |next_offset| will be returned that indicates where it left off.
pub async fn get_trace_file(&self, args: Value) -> Result<Value, Error> {
let path = args.get("path").ok_or_else(|| format_err!("GetTraceFile failed, no path"))?;
let path =
path.as_str().ok_or_else(|| format_err!("GetTraceFile failed, path not string"))?;
let offset = args.get("offset").and_then(Value::as_u64).unwrap_or(0);
let mut file = File::open(path)?;
file.seek(SeekFrom::Start(offset))?;
let mut contents = Vec::new();
file.by_ref().take(MAX_CHUNK_SIZE as u64).read_to_end(&mut contents)?;
let encoded_contents = BASE64_STANDARD.encode(&contents);
let mut result: HashMap<String, Value> = HashMap::new();
result.insert("data".to_owned(), to_value(encoded_contents)?);
if contents.len() == MAX_CHUNK_SIZE {
let new_offset = file.seek(SeekFrom::Current(0))?;
result.insert("next_offset".to_owned(), to_value(new_offset)?);
}
Ok(to_value(result)?)
}
}