"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
Object.defineProperty(exports, "__esModule", { value: true });
exports.CAPABILITY_REGISTRY = void 0;
exports.probeTraceCompleteness = probeTraceCompleteness;
/** Minimum row count below which data is considered "insufficient". */
const INSUFFICIENT_THRESHOLD = 3;
/**
 * Capability registry — the authoritative list of probed capabilities.
 * Order determines output order. Each entry maps to a section in
 * knowledge-data-sources.template.md for detailed capture guidance.
 */
const CAPABILITY_REGISTRY = [
    // ── Frame rendering (core for scrolling/jank analysis) ──
    {
        id: 'frame_rendering',
        displayName: '帧渲染/滑动分析',
        primaryTable: 'actual_frame_timeline_slice',
        excludedArchs: ['FLUTTER', 'WEBVIEW', 'GAME_ENGINE'],
        priority: 'critical',
    },
    {
        id: 'flutter_rendering',
        displayName: 'Flutter 渲染分析',
        // Flutter engine slices (1.ui, 1.raster, GPURasterizer) are in the generic slice table.
        // We use android_frames as the primary probe — it's populated when frame timeline + Flutter
        // pipeline detection succeeds. Falls back to architecture detection for flutter-specific analysis.
        primaryTable: 'android_frames',
        applicableArchs: ['FLUTTER'],
        priority: 'critical',
    },
    // ── Startup ──
    {
        id: 'startup',
        displayName: '启动性能分析',
        primaryTable: 'android_startups',
        priority: 'critical',
    },
    // ── IPC / synchronization ──
    {
        id: 'binder_ipc',
        displayName: 'Binder/IPC 分析',
        primaryTable: 'android_binder_txns',
        priority: 'recommended',
    },
    {
        id: 'lock_contention',
        displayName: '锁竞争分析',
        primaryTable: 'android_monitor_contention',
        priority: 'recommended',
    },
    // ── Memory ──
    {
        id: 'gc_memory',
        displayName: 'GC/内存分析',
        primaryTable: 'android_garbage_collection_events',
        priority: 'recommended',
    },
    {
        id: 'memory_pressure',
        displayName: '内存压力/LMK',
        primaryTable: 'android_oom_adj_intervals',
        priority: 'recommended',
    },
    // ── CPU ──
    {
        id: 'cpu_scheduling',
        displayName: 'CPU 调度分析',
        primaryTable: 'sched_slice',
        priority: 'critical',
    },
    {
        id: 'thermal_throttling',
        displayName: '热降频分析',
        primaryTable: 'android_dvfs_counters',
        priority: 'recommended',
    },
    // ── I/O ──
    {
        id: 'disk_io',
        displayName: 'I/O 分析',
        primaryTable: 'linux_active_block_io_operations_by_device',
        priority: 'optional',
    },
    // ── GPU ──
    {
        id: 'gpu',
        displayName: 'GPU 分析',
        primaryTable: 'gpu_slice',
        priority: 'optional',
    },
    // ── Profiling ──
    {
        id: 'cpu_profiling',
        displayName: 'CPU Profiling',
        primaryTable: 'linux_perf_samples_summary_tree',
        priority: 'optional',
    },
    // ── Input ──
    {
        id: 'input_latency',
        displayName: '输入延迟分析',
        primaryTable: 'android_input_events',
        priority: 'recommended',
    },
    // ── Display pipeline ──
    {
        id: 'surfaceflinger',
        displayName: 'SurfaceFlinger/Display 管线',
        primaryTable: 'android_surfaceflinger_workloads',
        priority: 'recommended',
    },
    // ── System state ──
    {
        id: 'device_state',
        displayName: '设备状态',
        primaryTable: 'android_screen_state',
        priority: 'optional',
    },
    {
        id: 'battery_power',
        displayName: '电池/功耗分析',
        primaryTable: 'android_battery_stats_state',
        priority: 'optional',
    },
    // ── IRQ ──
    {
        id: 'interrupts',
        displayName: 'IRQ/中断分析',
        primaryTable: 'linux_hard_irqs',
        priority: 'optional',
    },
    // ── ANR ──
    {
        id: 'anr',
        displayName: 'ANR 分析',
        primaryTable: 'android_anrs',
        priority: 'optional',
    },
    // ── Wattson power-modeling prerequisites (added 2026-05 per docs/skills-audit-2026-05.md M2.0) ──
    // Power skills require specific capture sources. Most production traces don't enable them,
    // so the prompt must surface gaps before Claude trusts empty tables.
    // These entries explicitly INCLUDE their stdlib modules before probing; otherwise sqlite_master
    // reports the tables as missing even when the trace data would support them.
    {
        id: 'power_rails',
        displayName: '功耗 Rails 实测（ODPM / PowerStats）',
        primaryTable: 'android_power_rails_counters',
        requiredModules: ['android.power_rails'],
        captureHint: '需要 android.power collect_power_rails，且设备硬件支持 power rails',
        priority: 'optional',
    },
    {
        id: 'battery_counters',
        displayName: '电池电量/电流采样（功耗前置）',
        primaryTable: 'android_battery_charge',
        requiredModules: ['android.battery'],
        captureHint: '需要 android.power battery_poll_ms 采样',
        priority: 'optional',
    },
    {
        id: 'cpu_freq_idle',
        displayName: 'CPU 频率/Idle 状态（Wattson 前置）',
        primaryTable: 'cpu_idle_counters',
        requiredModules: ['linux.cpu.idle'],
        captureHint: '需要 ftrace cpu_idle/cpu_frequency 相关事件；只有频率没有 idle 时 Wattson 估算不完整',
        priority: 'optional',
    },
    {
        id: 'gpu_work_period',
        displayName: 'GPU Work Period（Wattson GPU 前置）',
        primaryTable: 'android_gpu_work_period_track',
        requiredModules: ['android.gpu.work_period'],
        captureHint: '需要 android.gpu.work_period 数据源；否则无法做 GPU active region/能耗归因',
        priority: 'optional',
    },
];
exports.CAPABILITY_REGISTRY = CAPABILITY_REGISTRY;
async function loadProbeModules(tps, traceId) {
    const modules = Array.from(new Set(CAPABILITY_REGISTRY.flatMap(cap => cap.requiredModules ?? [])));
    if (modules.length === 0)
        return;
    for (const module of modules) {
        try {
            const result = await tps.query(traceId, `INCLUDE PERFETTO MODULE ${module};`);
            if (result?.error) {
                console.warn(`[TraceCompleteness] Failed to load probe module ${module}: ${result.error}`);
            }
        }
        catch (err) {
            console.warn(`[TraceCompleteness] Failed to load probe module ${module}:`, err.message);
        }
    }
}
function appendCaptureHint(reason, cap) {
    return cap.captureHint ? `${reason}；${cap.captureHint}` : reason;
}
/**
 * Probe trace data completeness.
 *
 * @param tps TraceProcessorService instance
 * @param traceId Active trace ID
 * @param architectureType Detected architecture (used for not_applicable filtering)
 * @returns TraceCompleteness diagnosis
 */
async function probeTraceCompleteness(tps, traceId, architectureType) {
    const t0 = Date.now();
    await loadProbeModules(tps, traceId);
    // ── Layer 1: Schema existence check ──────────────────────────────────────
    // Query sqlite_master for all table/view names relevant to our capabilities.
    const schemaResult = await tps.query(traceId, "SELECT name FROM sqlite_master WHERE type IN ('table', 'view')").catch(() => null);
    const existingTables = new Set();
    if (schemaResult?.rows) {
        for (const row of schemaResult.rows) {
            existingTables.add(row[0]);
        }
    }
    // ── Layer 2: Data existence check (only for tables that exist in schema) ──
    // Build a single UNION ALL query for efficiency.
    const tablesToProbe = CAPABILITY_REGISTRY
        .filter(cap => existingTables.has(cap.primaryTable))
        .map(cap => cap.primaryTable);
    const dataPresence = new Map(); // table → approximate row count
    if (tablesToProbe.length > 0) {
        // COUNT with LIMIT ${INSUFFICIENT_THRESHOLD} — only need to distinguish: 0 / 1..threshold / >threshold.
        const unionParts = tablesToProbe.map(t => `SELECT '${t}' AS tbl, COUNT(*) AS cnt FROM (SELECT 1 FROM ${t} LIMIT ${INSUFFICIENT_THRESHOLD})`);
        const countSql = unionParts.join(' UNION ALL ');
        try {
            const countResult = await tps.query(traceId, countSql);
            if (countResult?.rows) {
                for (const row of countResult.rows) {
                    dataPresence.set(row[0], row[1]);
                }
            }
        }
        catch (err) {
            // If the batch query fails (rare — e.g., one table has incompatible schema),
            // fall back to individual probes.
            console.warn('[TraceCompleteness] Batch count failed, falling back to individual probes:', err.message);
            await Promise.all(tablesToProbe.map(async (t) => {
                try {
                    const r = await tps.query(traceId, `SELECT COUNT(*) FROM (SELECT 1 FROM ${t} LIMIT ${INSUFFICIENT_THRESHOLD})`);
                    dataPresence.set(t, r?.rows?.[0]?.[0] ?? 0);
                }
                catch {
                    dataPresence.set(t, 0);
                }
            }));
        }
    }
    // ── Classify each capability ─────────────────────────────────────────────
    const available = [];
    const missingConfig = [];
    const notApplicable = [];
    const insufficient = [];
    for (const cap of CAPABILITY_REGISTRY) {
        // Architecture applicability check
        if (architectureType) {
            if (cap.applicableArchs && !cap.applicableArchs.includes(architectureType)) {
                notApplicable.push({
                    id: cap.id,
                    displayName: cap.displayName,
                    status: 'not_applicable',
                    primaryTable: cap.primaryTable,
                    reason: `当前架构 ${architectureType} 不适用`,
                });
                continue;
            }
            if (cap.excludedArchs?.includes(architectureType)) {
                notApplicable.push({
                    id: cap.id,
                    displayName: cap.displayName,
                    status: 'not_applicable',
                    primaryTable: cap.primaryTable,
                    reason: `${architectureType} 架构使用专用分析管线`,
                });
                continue;
            }
        }
        // Schema existence
        if (!existingTables.has(cap.primaryTable)) {
            missingConfig.push({
                id: cap.id,
                displayName: cap.displayName,
                status: 'missing_config_suspected',
                primaryTable: cap.primaryTable,
                reason: appendCaptureHint(`表 ${cap.primaryTable} 不存在 — 可能未开启所需 trace 配置`, cap),
            });
            continue;
        }
        // Data existence
        const rowCount = dataPresence.get(cap.primaryTable) ?? 0;
        if (rowCount === 0) {
            missingConfig.push({
                id: cap.id,
                displayName: cap.displayName,
                status: 'missing_config_suspected',
                primaryTable: cap.primaryTable,
                rowEstimate: 0,
                reason: appendCaptureHint(`表 ${cap.primaryTable} 存在但无数据 — 可能未开启所需 atrace/ftrace 配置，或场景未发生`, cap),
            });
        }
        else if (rowCount < INSUFFICIENT_THRESHOLD) {
            insufficient.push({
                id: cap.id,
                displayName: cap.displayName,
                status: 'insufficient_or_scene_absent',
                primaryTable: cap.primaryTable,
                rowEstimate: rowCount,
                reason: `仅 ${rowCount} 行数据 — trace 时长可能不够或场景未充分发生`,
            });
        }
        else {
            available.push({
                id: cap.id,
                displayName: cap.displayName,
                status: 'available',
                primaryTable: cap.primaryTable,
                rowEstimate: rowCount,
            });
        }
    }
    const elapsed = Date.now() - t0;
    console.log(`[TraceCompleteness] Probed ${CAPABILITY_REGISTRY.length} capabilities in ${elapsed}ms: ` +
        `available=${available.length}, missing=${missingConfig.length}, ` +
        `n/a=${notApplicable.length}, insufficient=${insufficient.length}`);
    return {
        available,
        missingConfig,
        notApplicable,
        insufficient,
        diagnosedAt: Date.now(),
    };
}
//# sourceMappingURL=traceCompletenessProber.js.map