blob: 01351565f50b9599baf25670a599b35eda8f7adb [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use datafusion::common::instant::Instant;
use once_cell::sync::Lazy;
use std::fs::{File, OpenOptions};
use std::io::{BufWriter, Write};
use std::sync::{Arc, Mutex};
pub(crate) static RECORDER: Lazy<Recorder> = Lazy::new(Recorder::new);
/// Log events using Chrome trace format JSON
/// https://github.com/catapult-project/catapult/blob/main/tracing/README.md
pub struct Recorder {
now: Instant,
writer: Arc<Mutex<BufWriter<File>>>,
}
impl Recorder {
pub fn new() -> Self {
let file = OpenOptions::new()
.create(true)
.append(true)
.open("comet-event-trace.json")
.expect("Error writing tracing");
let mut writer = BufWriter::new(file);
// Write start of JSON array. Note that there is no requirement to write
// the closing ']'.
writer
.write_all("[ ".as_bytes())
.expect("Error writing tracing");
Self {
now: Instant::now(),
writer: Arc::new(Mutex::new(writer)),
}
}
pub fn begin_task(&self, name: &str) {
self.log_event(name, "B")
}
pub fn end_task(&self, name: &str) {
self.log_event(name, "E")
}
pub fn log_memory_usage(&self, name: &str, usage_bytes: u64) {
let usage_mb = (usage_bytes as f64 / 1024.0 / 1024.0) as usize;
let json = format!(
"{{ \"name\": \"{name}\", \"cat\": \"PERF\", \"ph\": \"C\", \"pid\": 1, \"tid\": {}, \"ts\": {}, \"args\": {{ \"{name}\": {usage_mb} }} }},\n",
Self::get_thread_id(),
self.now.elapsed().as_micros()
);
let mut writer = self.writer.lock().unwrap();
writer
.write_all(json.as_bytes())
.expect("Error writing tracing");
}
fn log_event(&self, name: &str, ph: &str) {
let json = format!(
"{{ \"name\": \"{}\", \"cat\": \"PERF\", \"ph\": \"{ph}\", \"pid\": 1, \"tid\": {}, \"ts\": {} }},\n",
name,
Self::get_thread_id(),
self.now.elapsed().as_micros()
);
let mut writer = self.writer.lock().unwrap();
writer
.write_all(json.as_bytes())
.expect("Error writing tracing");
}
fn get_thread_id() -> u64 {
let thread_id = std::thread::current().id();
format!("{thread_id:?}")
.trim_start_matches("ThreadId(")
.trim_end_matches(")")
.parse()
.expect("Error parsing thread id")
}
}
pub(crate) fn trace_begin(name: &str) {
RECORDER.begin_task(name);
}
pub(crate) fn trace_end(name: &str) {
RECORDER.end_task(name);
}
pub(crate) fn log_memory_usage(name: &str, value: u64) {
RECORDER.log_memory_usage(name, value);
}
pub(crate) fn with_trace<T, F>(label: &str, tracing_enabled: bool, f: F) -> T
where
F: FnOnce() -> T,
{
if tracing_enabled {
trace_begin(label);
}
let result = f();
if tracing_enabled {
trace_end(label);
}
result
}
pub(crate) async fn with_trace_async<F, Fut, T>(label: &str, tracing_enabled: bool, f: F) -> T
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = T>,
{
if tracing_enabled {
trace_begin(label);
}
let result = f().await;
if tracing_enabled {
trace_end(label);
}
result
}