use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BugSeverity {
Critical,
High,
Medium,
FalsePositive,
}
impl fmt::Display for BugSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Critical => write!(f, "P0-CRITICAL"),
Self::High => write!(f, "P1-HIGH"),
Self::Medium => write!(f, "P2-MEDIUM"),
Self::FalsePositive => write!(f, "FALSE-POSITIVE"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PtxBugClass {
SharedMemU64Addressing,
LoopBranchToEnd,
MissingBarrierSync,
EarlyExitBeforeBarrier,
NonInPlaceLoopAccumulator,
RegisterSpills,
HighRegisterPressure,
PredicateOverflow,
PlaceholderCode,
EmptyLoopBody,
MissingBoundsCheck,
RedundantMoves,
UnoptimizedMemoryPattern,
DeadCode,
InvalidSyntaxAccepted,
MissingEntryPoint,
}
impl PtxBugClass {
#[must_use]
pub fn severity(&self) -> BugSeverity {
match self {
Self::SharedMemU64Addressing
| Self::LoopBranchToEnd
| Self::MissingBarrierSync
| Self::EarlyExitBeforeBarrier => BugSeverity::Critical,
Self::NonInPlaceLoopAccumulator
| Self::RegisterSpills
| Self::HighRegisterPressure
| Self::PredicateOverflow
| Self::PlaceholderCode
| Self::EmptyLoopBody
| Self::MissingBoundsCheck => BugSeverity::High,
Self::RedundantMoves | Self::UnoptimizedMemoryPattern | Self::DeadCode => {
BugSeverity::Medium
}
Self::InvalidSyntaxAccepted | Self::MissingEntryPoint => BugSeverity::FalsePositive,
}
}
#[must_use]
pub fn code(&self) -> &'static str {
match self {
Self::SharedMemU64Addressing => "SHARED_U64",
Self::LoopBranchToEnd => "LOOP_BRANCH_END",
Self::MissingBarrierSync => "MISSING_BARRIER",
Self::EarlyExitBeforeBarrier => "EARLY_EXIT_BARRIER",
Self::NonInPlaceLoopAccumulator => "NON_INPLACE_ACCUM",
Self::RegisterSpills => "REG_SPILLS",
Self::HighRegisterPressure => "HIGH_REG_PRESSURE",
Self::PredicateOverflow => "PRED_OVERFLOW",
Self::PlaceholderCode => "PLACEHOLDER_CODE",
Self::EmptyLoopBody => "EMPTY_LOOP",
Self::MissingBoundsCheck => "NO_BOUNDS_CHECK",
Self::RedundantMoves => "REDUNDANT_MOV",
Self::UnoptimizedMemoryPattern => "UNOPT_MEM",
Self::DeadCode => "DEAD_CODE",
Self::InvalidSyntaxAccepted => "INVALID_SYNTAX",
Self::MissingEntryPoint => "NO_ENTRY",
}
}
}
impl fmt::Display for PtxBugClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.code())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PtxBug {
pub class: PtxBugClass,
pub line: usize,
pub instruction: String,
pub message: String,
pub fix: Option<String>,
}
impl PtxBug {
#[must_use]
pub fn severity(&self) -> BugSeverity {
self.class.severity()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PtxBugReport {
pub kernel_name: Option<String>,
pub bugs: Vec<PtxBug>,
pub lines_analyzed: usize,
pub strict_mode: bool,
}
impl PtxBugReport {
#[must_use]
pub fn is_valid(&self) -> bool {
!self
.bugs
.iter()
.any(|b| b.severity() == BugSeverity::Critical)
}
#[must_use]
pub fn has_bugs(&self) -> bool {
!self.bugs.is_empty()
}
#[must_use]
pub fn count_by_severity(&self, severity: BugSeverity) -> usize {
self.bugs
.iter()
.filter(|b| b.severity() == severity)
.count()
}
#[must_use]
pub fn has_bug(&self, class: &PtxBugClass) -> bool {
self.bugs.iter().any(|b| &b.class == class)
}
#[must_use]
pub fn bugs_of_class(&self, class: &PtxBugClass) -> Vec<&PtxBug> {
self.bugs.iter().filter(|b| &b.class == class).collect()
}
#[must_use]
pub fn format_report(&self) -> String {
let mut output = String::new();
output.push_str(
"╔══════════════════════════════════════════════════════════════════════════════╗\n",
);
output.push_str(
"║ PTX BUG HUNTING REPORT ║\n",
);
output.push_str(
"╚══════════════════════════════════════════════════════════════════════════════╝\n\n",
);
if let Some(name) = &self.kernel_name {
output.push_str(&format!("Kernel: {}\n", name));
}
output.push_str(&format!("PTX Lines Analyzed: {}\n\n", self.lines_analyzed));
let critical = self.count_by_severity(BugSeverity::Critical);
let high = self.count_by_severity(BugSeverity::High);
let medium = self.count_by_severity(BugSeverity::Medium);
let false_pos = self.count_by_severity(BugSeverity::FalsePositive);
output.push_str(&format!("P0 CRITICAL BUGS: {}\n", critical));
if critical > 0 {
output.push_str("──────────────────\n");
for (i, bug) in self
.bugs
.iter()
.filter(|b| b.severity() == BugSeverity::Critical)
.enumerate()
{
output.push_str(&format!(" BUG-{:03}: {}\n", i + 1, bug.class));
if bug.line > 0 {
output.push_str(&format!(" Line {}: {}\n", bug.line, bug.instruction));
}
output.push_str(&format!(" Impact: {}\n", bug.message));
if let Some(fix) = &bug.fix {
output.push_str(&format!(" Fix: {}\n", fix));
}
output.push('\n');
}
}
output.push_str(&format!("\nP1 HIGH BUGS: {}\n", high));
if high > 0 {
output.push_str("─────────────────\n");
for bug in self
.bugs
.iter()
.filter(|b| b.severity() == BugSeverity::High)
{
output.push_str(&format!(" {}: {}\n", bug.class, bug.message));
}
}
output.push_str(&format!("\nP2 MEDIUM BUGS: {}\n", medium));
if medium > 0 {
output.push_str("─────────────────\n");
for bug in self
.bugs
.iter()
.filter(|b| b.severity() == BugSeverity::Medium)
{
output.push_str(&format!(" {}: {}\n", bug.class, bug.message));
}
}
output.push_str(&format!("\nFALSE POSITIVES DETECTED: {}\n", false_pos));
output.push_str("\nSUMMARY\n═══════\n");
output.push_str(&format!(" Total Bugs: {}\n", self.bugs.len()));
output.push_str(&format!(" P0 Critical: {}", critical));
if critical > 0 {
output.push_str(" ← BLOCKS RELEASE");
}
output.push('\n');
output.push_str(&format!(" P1 High: {}\n", high));
output.push_str(&format!(" P2 Medium: {}\n", medium));
output
}
}