trace.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. use crate::flog;
  2. use crate::interpreter::Context;
  3. use crate::jets::bits::util::rap;
  4. use crate::jets::form::util::scow;
  5. use crate::mem::NockStack;
  6. use crate::mug::met3_usize;
  7. use crate::noun::{Atom, DirectAtom, IndirectAtom, Noun};
  8. use either::Either::*;
  9. use json::object;
  10. use nockvm_macros::tas;
  11. use std::fs::{create_dir_all, File};
  12. use std::io::{Error, Write};
  13. use std::path::PathBuf;
  14. use std::result::Result;
  15. use std::time::Instant;
  16. crate::gdb!();
  17. pub struct TraceInfo {
  18. pub file: File,
  19. pub pid: u32,
  20. pub process_start: Instant,
  21. }
  22. pub struct TraceStack {
  23. pub start: Instant,
  24. pub path: Noun,
  25. pub next: *const TraceStack,
  26. }
  27. pub fn create_trace_file(pier_path: PathBuf) -> Result<TraceInfo, Error> {
  28. let mut trace_dir_path = pier_path.clone();
  29. trace_dir_path.push(".urb");
  30. trace_dir_path.push("put");
  31. trace_dir_path.push("trace");
  32. create_dir_all(&trace_dir_path)?;
  33. let trace_path: PathBuf;
  34. let mut trace_idx = 0u32;
  35. loop {
  36. let mut prospective_path = trace_dir_path.clone();
  37. prospective_path.push(format!("{}.json", trace_idx));
  38. if prospective_path.exists() {
  39. trace_idx += 1;
  40. } else {
  41. trace_path = prospective_path.clone();
  42. break;
  43. }
  44. }
  45. let file = File::create(trace_path)?;
  46. let process_start = Instant::now();
  47. let pid = std::process::id();
  48. Ok(TraceInfo {
  49. file,
  50. pid,
  51. process_start,
  52. })
  53. }
  54. /// Write metadata to trace file
  55. pub fn write_metadata(info: &mut TraceInfo) -> Result<(), Error> {
  56. info.file.write_all("[ ".as_bytes())?;
  57. (object! {
  58. "name" => "process_name",
  59. "ph" => "M",
  60. "pid" => info.pid,
  61. "args" => object! { "name" => "urbit", },
  62. })
  63. .write(&mut info.file)?;
  64. info.file.write_all(",\n".as_bytes())?;
  65. (object! {
  66. "name" => "thread_name",
  67. "ph" => "M",
  68. "pid" => info.pid,
  69. "tid" => 1,
  70. "args" => object! { "name" => "Event Processing", },
  71. })
  72. .write(&mut info.file)?;
  73. info.file.write_all(",\n".as_bytes())?;
  74. (object! {
  75. "name" => "thread_sort_index",
  76. "ph" => "M",
  77. "pid" => info.pid,
  78. "tid" => 1,
  79. "args" => object! { "sort_index" => 1, },
  80. })
  81. .write(&mut info.file)?;
  82. info.file.write_all(",\n".as_bytes())?;
  83. Ok(())
  84. }
  85. /// Abort writing to trace file if an error is encountered.
  86. ///
  87. /// This should result in a well-formed partial trace file.
  88. pub fn write_serf_trace_safe(context: &mut Context, name: &str, start: Instant) {
  89. if let Err(e) = write_serf_trace(
  90. context.trace_info.as_mut().unwrap_or_else(|| {
  91. panic!(
  92. "Panicked at {}:{} (git sha: {:?})",
  93. file!(),
  94. line!(),
  95. option_env!("GIT_SHA")
  96. )
  97. }),
  98. name,
  99. start,
  100. ) {
  101. flog!(context, "\rserf: error writing event trace to file: {:?}", e);
  102. let info = &mut context.trace_info;
  103. *info = None;
  104. }
  105. }
  106. pub fn write_serf_trace(info: &mut TraceInfo, name: &str, start: Instant) -> Result<(), Error> {
  107. let ts = start
  108. .saturating_duration_since(info.process_start)
  109. .as_micros() as f64;
  110. let dur = Instant::now().saturating_duration_since(start).as_micros() as f64;
  111. let obj = object! {
  112. "cat" => "event",
  113. "name" => name,
  114. "ph" => "X",
  115. "pid" => info.pid,
  116. "tid" => 1,
  117. "ts" => ts,
  118. "dur" => dur,
  119. };
  120. let _ = obj.write(&mut info.file);
  121. info.file.write_all(",\n".as_bytes())?;
  122. Ok(())
  123. }
  124. pub unsafe fn write_nock_trace(
  125. stack: &mut NockStack,
  126. info: &mut TraceInfo,
  127. mut trace_stack: *const TraceStack,
  128. ) -> Result<(), Error> {
  129. let now = Instant::now();
  130. while !trace_stack.is_null() {
  131. let ts = (*trace_stack)
  132. .start
  133. .saturating_duration_since(info.process_start)
  134. .as_micros() as f64;
  135. let dur = now
  136. .saturating_duration_since((*trace_stack).start)
  137. .as_micros() as f64;
  138. // Don't write out traces less than 33us
  139. // (same threshhold used in vere)
  140. if dur < 33.0 {
  141. trace_stack = (*trace_stack).next;
  142. continue;
  143. }
  144. let pc = path_to_cord(stack, (*trace_stack).path);
  145. let pc_len = met3_usize(pc);
  146. let pc_bytes = &pc.as_ne_bytes()[0..pc_len];
  147. let pc_str = match std::str::from_utf8(pc_bytes) {
  148. Ok(valid) => valid,
  149. Err(error) => {
  150. let (valid, _) = pc_bytes.split_at(error.valid_up_to());
  151. unsafe { std::str::from_utf8_unchecked(valid) }
  152. }
  153. };
  154. let obj = object! {
  155. "cat" => "nock",
  156. "name" => pc_str,
  157. "ph" => "X",
  158. "pid" => info.pid,
  159. "tid" => 1,
  160. "ts" => ts,
  161. "dur" => dur,
  162. };
  163. let _ = obj.write(&mut info.file);
  164. info.file.write_all(",\n".as_bytes())?;
  165. trace_stack = (*trace_stack).next;
  166. }
  167. Ok(())
  168. }
  169. // XX: Need Rust string interpolation helper that doesn't allocate
  170. pub fn path_to_cord(stack: &mut NockStack, path: Noun) -> Atom {
  171. let mut cursor = path;
  172. let mut length = 0usize;
  173. // count how much size we need
  174. while let Ok(c) = cursor.as_cell() {
  175. unsafe {
  176. match c.head().as_either_atom_cell() {
  177. Left(a) => {
  178. length += 1;
  179. length += met3_usize(a);
  180. }
  181. Right(ch) => {
  182. if let Ok(nm) = ch.head().as_atom() {
  183. if let Ok(kv) = ch.tail().as_atom() {
  184. let kvt = scow(stack, DirectAtom::new_unchecked(tas!(b"ud")), kv)
  185. .expect("scow should succeed in path_to_cord");
  186. let kvc =
  187. rap(stack, 3, kvt).expect("rap should succeed in path_to_cord");
  188. length += 1;
  189. length += met3_usize(nm);
  190. length += met3_usize(kvc);
  191. }
  192. }
  193. }
  194. }
  195. }
  196. cursor = c.tail();
  197. }
  198. // reset cursor, then actually write the path
  199. cursor = path;
  200. let mut idx = 0;
  201. let (mut deres, buffer) = unsafe { IndirectAtom::new_raw_mut_bytes(stack, length) };
  202. let slash = (b"/")[0];
  203. while let Ok(c) = cursor.as_cell() {
  204. unsafe {
  205. match c.head().as_either_atom_cell() {
  206. Left(a) => {
  207. buffer[idx] = slash;
  208. idx += 1;
  209. let bytelen = met3_usize(a);
  210. buffer[idx..idx + bytelen].copy_from_slice(&a.as_ne_bytes()[0..bytelen]);
  211. idx += bytelen;
  212. }
  213. Right(ch) => {
  214. if let Ok(nm) = ch.head().as_atom() {
  215. if let Ok(kv) = ch.tail().as_atom() {
  216. let kvt = scow(stack, DirectAtom::new_unchecked(tas!(b"ud")), kv)
  217. .expect("scow should succeed in path_to_cord");
  218. let kvc =
  219. rap(stack, 3, kvt).expect("rap should succeed in path_to_cord");
  220. buffer[idx] = slash;
  221. idx += 1;
  222. let nmlen = met3_usize(nm);
  223. buffer[idx..idx + nmlen].copy_from_slice(&nm.as_ne_bytes()[0..nmlen]);
  224. idx += nmlen;
  225. let kvclen = met3_usize(kvc);
  226. buffer[idx..idx + kvclen]
  227. .copy_from_slice(&kvc.as_ne_bytes()[0..kvclen]);
  228. idx += kvclen;
  229. }
  230. }
  231. }
  232. }
  233. }
  234. cursor = c.tail();
  235. }
  236. unsafe { deres.normalize_as_atom() }
  237. }