blob: ee3fb70c952075f2c555cd60091ee3fca7976024 [file] [log] [blame]
// 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;
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(format_err!("GetTraceFile failed, no path"))?;
let path = path.as_str().ok_or(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::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)?)
}
}