1use super::debug_info::{
2 SectionInfo, SharedLibraryInfo, SourceCodeInfo, SourceFileGroup, TargetDebugInfo,
3};
4use super::source_path::SourcePathInfo;
5use unicode_width::UnicodeWidthStr;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum TraceStatus {
10 Active,
11 Disabled,
12 Failed,
13}
14
15impl std::fmt::Display for TraceStatus {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 match self {
18 TraceStatus::Active => write!(f, "Active"),
19 TraceStatus::Disabled => write!(f, "Disabled"),
20 TraceStatus::Failed => write!(f, "Failed"),
21 }
22 }
23}
24
25impl TraceStatus {
26 pub fn to_emoji(&self) -> String {
28 match self {
29 TraceStatus::Active => "β
".to_string(),
30 TraceStatus::Disabled => "βΈοΈ".to_string(),
31 TraceStatus::Failed => "β".to_string(),
32 }
33 }
34
35 pub fn from_string(s: &str) -> Self {
37 match s {
38 "Active" => TraceStatus::Active,
39 "Disabled" => TraceStatus::Disabled,
40 "Failed" => TraceStatus::Failed,
41 _ => TraceStatus::Failed, }
43 }
44}
45
46#[derive(Debug, Clone)]
48pub enum RuntimeCommand {
49 ExecuteScript {
50 command: String,
51 selected_index: Option<usize>,
52 },
53 RequestSourceCode, DisableTrace(u32), EnableTrace(u32), DisableAllTraces, EnableAllTraces, DeleteTrace(u32), DeleteAllTraces, InfoFunction {
61 target: String,
62 verbose: bool,
63 }, InfoLine {
65 target: String,
66 verbose: bool,
67 }, InfoAddress {
69 target: String,
70 verbose: bool,
71 }, InfoTrace {
73 trace_id: Option<u32>,
74 }, InfoTraceAll,
76 InfoSource, InfoShare, InfoFile, SaveTraces {
80 filename: Option<String>,
81 filter: crate::components::command_panel::trace_persistence::SaveFilter,
82 }, LoadTraces {
84 filename: String,
85 traces: Vec<TraceDefinition>,
86 }, SrcPathList,
88 SrcPathAddDir {
89 dir: String,
90 },
91 SrcPathAddMap {
92 from: String,
93 to: String,
94 },
95 SrcPathRemove {
96 pattern: String,
97 },
98 SrcPathClear,
99 SrcPathReset,
100 Shutdown,
101}
102
103#[derive(Debug, Clone)]
105pub struct TraceDefinition {
106 pub target: String,
107 pub script: String,
108 pub enabled: bool,
109 pub selected_index: Option<usize>,
110}
111
112#[derive(Debug, Clone)]
114pub struct TraceLoadDetail {
115 pub target: String,
116 pub trace_id: Option<u32>,
117 pub status: LoadStatus,
118 pub error: Option<String>,
119}
120
121#[derive(Debug, Clone)]
123pub enum LoadStatus {
124 Created, CreatedDisabled, Failed, Skipped, }
129
130#[derive(Debug, Clone)]
132pub enum ExecutionStatus {
133 Success,
134 Failed(String), Skipped(String), }
137
138#[derive(Debug, Clone)]
140pub struct ScriptExecutionResult {
141 pub pc_address: u64,
142 pub target_name: String,
143 pub binary_path: String, pub status: ExecutionStatus,
145 pub source_file: Option<String>,
146 pub source_line: Option<u32>,
147 pub is_inline: Option<bool>,
148}
149
150#[derive(Debug, Clone)]
152pub struct ScriptCompilationDetails {
153 pub trace_ids: Vec<u32>, pub results: Vec<ScriptExecutionResult>,
155 pub total_count: usize,
156 pub success_count: usize,
157 pub failed_count: usize,
158}
159
160#[derive(Debug, Clone)]
161pub enum RuntimeStatus {
162 DwarfLoadingStarted,
163 DwarfLoadingCompleted {
164 symbols_count: usize,
165 },
166 DwarfLoadingFailed(String),
167 ScriptCompilationCompleted {
168 details: ScriptCompilationDetails, },
170 UprobeAttached {
171 function: String,
172 address: u64,
173 },
174 UprobeDetached {
175 function: String,
176 },
177 SourceCodeLoaded(SourceCodeInfo),
178 SourceCodeLoadFailed(String),
179 TraceEnabled {
180 trace_id: u32,
181 },
182 TraceDisabled {
183 trace_id: u32,
184 },
185 AllTracesEnabled {
186 count: usize,
187 error: Option<String>, },
189 AllTracesDisabled {
190 count: usize,
191 error: Option<String>, },
193 TraceEnableFailed {
194 trace_id: u32,
195 error: String,
196 },
197 TraceDisableFailed {
198 trace_id: u32,
199 error: String,
200 },
201 TraceDeleted {
202 trace_id: u32,
203 },
204 AllTracesDeleted {
205 count: usize,
206 error: Option<String>, },
208 TraceDeleteFailed {
209 trace_id: u32,
210 error: String,
211 },
212 InfoFunctionResult {
213 target: String,
214 info: TargetDebugInfo,
215 verbose: bool,
216 },
217 InfoFunctionFailed {
218 target: String,
219 error: String,
220 },
221 InfoLineResult {
222 target: String,
223 info: TargetDebugInfo,
224 verbose: bool,
225 },
226 InfoLineFailed {
227 target: String,
228 error: String,
229 },
230 InfoAddressResult {
231 target: String,
232 info: TargetDebugInfo,
233 verbose: bool,
234 },
235 InfoAddressFailed {
236 target: String,
237 error: String,
238 },
239 TraceInfo {
241 trace_id: u32,
242 target: String,
243 status: TraceStatus,
244 pid: Option<u32>,
245 host_pid: Option<u32>,
246 binary: String,
247 script_preview: Option<String>,
248 pc: u64,
249 },
250 TraceInfoAll {
252 summary: TraceSummaryInfo,
253 traces: Vec<TraceDetailInfo>,
254 },
255 TraceInfoFailed {
257 trace_id: u32,
258 error: String,
259 },
260 FileInfo {
262 groups: Vec<SourceFileGroup>,
263 },
264 FileInfoFailed {
266 error: String,
267 },
268 TracesSaved {
270 filename: String,
271 saved_count: usize,
272 total_count: usize,
273 },
274 TracesSaveFailed {
276 error: String,
277 },
278 TracesLoaded {
280 filename: String,
281 total_count: usize,
282 success_count: usize,
283 failed_count: usize,
284 disabled_count: usize,
285 details: Vec<TraceLoadDetail>,
286 },
287 TracesLoadFailed {
289 filename: String,
290 error: String,
291 },
292 ShareInfo {
294 libraries: Vec<SharedLibraryInfo>,
295 },
296 ShareInfoFailed {
298 error: String,
299 },
300 ExecutableFileInfo {
302 file_path: String,
303 file_type: String,
304 entry_point: Option<u64>,
305 has_symbols: bool,
306 has_debug_info: bool,
307 debug_file_path: Option<String>,
308 text_section: Option<SectionInfo>,
309 data_section: Option<SectionInfo>,
310 mode_description: String,
311 },
312 ExecutableFileInfoFailed {
314 error: String,
315 },
316 DwarfModuleDiscovered {
318 module_path: String,
319 total_modules: usize,
320 },
321 DwarfModuleLoadingStarted {
322 module_path: String,
323 current: usize,
324 total: usize,
325 },
326 DwarfModuleLoadingCompleted {
327 module_path: String,
328 stats: ModuleLoadingStats,
329 current: usize,
330 total: usize,
331 },
332 DwarfModuleLoadingFailed {
333 module_path: String,
334 error: String,
335 current: usize,
336 total: usize,
337 },
338 SrcPathInfo {
339 info: SourcePathInfo,
340 },
341 SrcPathUpdated {
342 message: String,
343 },
344 SrcPathFailed {
345 error: String,
346 },
347 TraceBackpressure {
349 dropped_since_last: u64,
350 dropped_total: u64,
351 queue_capacity: usize,
352 },
353 EbpfOutputLoss {
355 trace_id: u32,
356 target_display: String,
357 lost_since_last: u64,
358 lost_total: u64,
359 },
360}
361
362#[derive(Debug, Clone)]
364pub struct ModuleLoadingStats {
365 pub functions: usize,
366 pub variables: usize,
367 pub types: usize,
368 pub debug_source: String,
369 pub debug_source_path: Option<String>,
370 pub load_time_ms: u64,
371}
372
373#[derive(Debug, Clone)]
375pub struct TraceSummaryInfo {
376 pub total: usize,
377 pub active: usize,
378 pub disabled: usize,
379}
380
381#[derive(Debug, Clone)]
383pub struct TraceDetailInfo {
384 pub trace_id: u32,
385 pub target_display: String,
386 pub binary_path: String,
387 pub pc: u64,
388 pub status: TraceStatus,
389 pub duration: String, }
391
392impl TraceDetailInfo {
393 pub fn format_line(&self) -> String {
395 let binary_name = std::path::Path::new(&self.binary_path)
397 .file_name()
398 .and_then(|name| name.to_str())
399 .unwrap_or(&self.binary_path);
400
401 format!(
402 "#{} | {}+0x{:x} | {} ({}) ",
403 self.trace_id, binary_name, self.pc, self.target_display, self.status
404 )
405 }
406}
407
408impl RuntimeStatus {
409 pub fn format_trace_info(&self) -> Option<String> {
411 match self {
412 RuntimeStatus::TraceInfo {
413 trace_id,
414 target,
415 status,
416 pid,
417 host_pid,
418 binary,
419 script_preview,
420 pc,
421 } => {
422 let mut result =
424 format!("π Trace [{}] {} {}\n", trace_id, status.to_emoji(), status);
425
426 let binary_name = std::path::Path::new(binary)
428 .file_name()
429 .and_then(|name| name.to_str())
430 .unwrap_or(binary);
431
432 let mut fields: Vec<(&str, String)> = Vec::new();
433 fields.push(("π― Target", target.clone()));
434 fields.push(("π¦ Binary", binary.clone()));
435 fields.push(("π Address", format!("{binary_name}+0x{pc:x}")));
436 match (pid, host_pid) {
437 (Some(proc_pid), Some(host_pid_val)) if proc_pid != host_pid_val => {
438 fields.push(("π·οΈ PID(proc)", proc_pid.to_string()));
439 fields.push(("π·οΈ PID(host)", host_pid_val.to_string()));
440 }
441 (Some(proc_pid), _) => {
442 fields.push(("π·οΈ PID", proc_pid.to_string()));
443 }
444 (None, Some(host_pid_val)) => {
445 fields.push(("π·οΈ PID(host)", host_pid_val.to_string()));
446 }
447 (None, None) => {}
448 }
449 if let Some(ref script) = script_preview {
450 fields.push(("π Script", script.clone()));
451 }
452
453 let max_key_width = fields.iter().map(|(k, _)| k.width()).max().unwrap_or(0);
455
456 for (key, value) in fields {
457 let key_width = key.width();
458 let pad = max_key_width.saturating_sub(key_width);
459 let spaces = " ".repeat(pad);
460 result.push_str(&format!(" {key}{spaces}: {value}\n"));
461 }
462
463 Some(result)
464 }
465 _ => None,
466 }
467 }
468
469 pub fn format_trace_info_styled(&self) -> Option<Vec<ratatui::text::Line<'static>>> {
471 use crate::components::command_panel::style_builder::{StylePresets, StyledLineBuilder};
472 use ratatui::text::Line;
473
474 match self {
475 RuntimeStatus::TraceInfo {
476 trace_id,
477 target,
478 status,
479 pid,
480 host_pid,
481 binary,
482 script_preview: _,
483 pc,
484 } => {
485 let mut lines = Vec::new();
486
487 lines.push(
489 StyledLineBuilder::new()
490 .title(format!(
491 "π Trace [{}] {} {}",
492 trace_id,
493 status.to_emoji(),
494 status
495 ))
496 .build(),
497 );
498
499 let binary_name = std::path::Path::new(binary)
500 .file_name()
501 .and_then(|name| name.to_str())
502 .unwrap_or(binary)
503 .to_string();
504
505 lines.push(
506 StyledLineBuilder::new()
507 .text(" ")
508 .key("π― Target:")
509 .text(" ")
510 .value(target)
511 .build(),
512 );
513 lines.push(
514 StyledLineBuilder::new()
515 .text(" ")
516 .key("π¦ Binary:")
517 .text(" ")
518 .value(binary)
519 .build(),
520 );
521 lines.push(
522 StyledLineBuilder::new()
523 .text(" ")
524 .key("π Address:")
525 .text(" ")
526 .value(format!("{binary_name}+0x{pc:x}"))
527 .build(),
528 );
529
530 match (pid, host_pid) {
531 (Some(proc_pid), Some(host_pid_val)) if proc_pid != host_pid_val => {
532 lines.push(
533 StyledLineBuilder::new()
534 .text(" ")
535 .key("π·οΈ PID(proc):")
536 .text(" ")
537 .value(proc_pid.to_string())
538 .build(),
539 );
540 lines.push(
541 StyledLineBuilder::new()
542 .text(" ")
543 .key("π·οΈ PID(host):")
544 .text(" ")
545 .value(host_pid_val.to_string())
546 .build(),
547 );
548 }
549 (Some(proc_pid), _) => {
550 lines.push(
551 StyledLineBuilder::new()
552 .text(" ")
553 .key("π·οΈ PID:")
554 .text(" ")
555 .value(proc_pid.to_string())
556 .build(),
557 );
558 }
559 (None, Some(host_pid_val)) => {
560 lines.push(
561 StyledLineBuilder::new()
562 .text(" ")
563 .key("π·οΈ PID(host):")
564 .text(" ")
565 .value(host_pid_val.to_string())
566 .build(),
567 );
568 }
569 (None, None) => {}
570 }
571
572 Some(lines)
573 }
574 RuntimeStatus::TraceInfoAll { summary, traces } => {
575 let mut lines = Vec::new();
576 lines.push(
578 StyledLineBuilder::new()
579 .title(format!(
580 "π All Traces ({} total, {} active):",
581 summary.total, summary.active
582 ))
583 .build(),
584 );
585 lines.push(Line::from(""));
586
587 for t in traces {
588 let binary_name = std::path::Path::new(&t.binary_path)
589 .file_name()
590 .and_then(|name| name.to_str())
591 .unwrap_or(&t.binary_path)
592 .to_string();
593 let status_style = match t.status {
594 TraceStatus::Active => StylePresets::SUCCESS,
595 TraceStatus::Disabled => StylePresets::LOCATION,
596 TraceStatus::Failed => StylePresets::ERROR,
597 };
598 let line = StyledLineBuilder::new()
599 .text(" ")
600 .styled(format!("#{}", t.trace_id), StylePresets::ADDRESS)
601 .text(" | ")
602 .styled(format!("{}+0x{:x}", binary_name, t.pc), StylePresets::KEY)
603 .text(" | ")
604 .value(&t.target_display)
605 .text(" (")
606 .styled(t.status.to_string(), status_style)
607 .text(")")
608 .build();
609 lines.push(line);
610 }
611
612 Some(lines)
613 }
614 _ => None,
615 }
616 }
617}