Skip to content

Commit b3186ab

Browse files
committed
debug
1 parent 032200a commit b3186ab

File tree

2 files changed

+175
-1
lines changed

2 files changed

+175
-1
lines changed

src/app/Flash.jsx

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useEffect, useRef, useState } from 'react'
22

3-
import { FlashManager, StepCode, ErrorCode, DeviceType } from '../utils/manager'
3+
import { FlashManager, StepCode, ErrorCode, DeviceType, DUMP_GPT_MODE } from '../utils/manager'
44
import { useImageManager } from '../utils/image'
55
import { isLinux, isWindows } from '../utils/platform'
66
import config from '../config'
@@ -517,6 +517,91 @@ function LinuxUnbind({ onNext }) {
517517
)
518518
}
519519

520+
// GPT Dump diagnostic mode
521+
function GptDumpMode({ qdlManager, imageManager }) {
522+
const [gptDump, setGptDump] = useState(null)
523+
const [loading, setLoading] = useState(false)
524+
const [copied, setCopied] = useState(false)
525+
const [ready, setReady] = useState(false)
526+
527+
useEffect(() => {
528+
if (!imageManager.current) return
529+
fetch(config.loader.url)
530+
.then((res) => res.arrayBuffer())
531+
.then((programmer) => {
532+
qdlManager.current = new FlashManager(programmer, {})
533+
qdlManager.current.initialize(imageManager.current).then(() => setReady(true))
534+
})
535+
}, [imageManager.current])
536+
537+
const handleDump = async () => {
538+
setLoading(true)
539+
const result = await qdlManager.current.dumpGpt()
540+
setGptDump(result)
541+
setLoading(false)
542+
}
543+
544+
const handleCopy = () => {
545+
navigator.clipboard.writeText(gptDump)
546+
setCopied(true)
547+
setTimeout(() => setCopied(false), 2000)
548+
}
549+
550+
return (
551+
<div className="flex flex-col items-center justify-center h-full gap-6 p-8">
552+
<div className="text-center">
553+
<h1 className="text-3xl font-bold mb-2">GPT Diagnostic Mode</h1>
554+
<p className="text-xl text-gray-600">
555+
Connect your device to dump partition table info
556+
</p>
557+
</div>
558+
559+
{!gptDump ? (
560+
<button
561+
onClick={handleDump}
562+
disabled={loading || !ready}
563+
className={`px-8 py-3 text-xl font-semibold rounded-full transition-colors ${
564+
loading || !ready
565+
? 'bg-gray-300 text-gray-500 cursor-not-allowed'
566+
: 'bg-[#51ff00] hover:bg-[#45e000] active:bg-[#3acc00] text-black'
567+
}`}
568+
>
569+
{loading ? 'Reading...' : !ready ? 'Initializing...' : 'Connect & Dump GPT'}
570+
</button>
571+
) : (
572+
<>
573+
<textarea
574+
readOnly
575+
value={gptDump}
576+
className="w-full max-w-3xl h-96 p-4 font-mono text-sm bg-gray-900 text-gray-100 rounded-lg"
577+
/>
578+
<div className="flex gap-4">
579+
<button
580+
onClick={handleCopy}
581+
className="px-6 py-2 text-lg font-semibold rounded-full bg-blue-600 hover:bg-blue-500 text-white transition-colors"
582+
>
583+
{copied ? 'Copied!' : 'Copy to Clipboard'}
584+
</button>
585+
<button
586+
onClick={() => setGptDump(null)}
587+
className="px-6 py-2 text-lg font-semibold rounded-full bg-gray-300 hover:bg-gray-400 text-black transition-colors"
588+
>
589+
Dump Again
590+
</button>
591+
</div>
592+
<p className="text-gray-500">
593+
Send this to{' '}
594+
<a href="https://discord.comma.ai" target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline">
595+
Discord
596+
</a>
597+
{' '}for debugging
598+
</p>
599+
</>
600+
)}
601+
</div>
602+
)
603+
}
604+
520605
// WebUSB connection screen - shows while waiting for user to select device
521606
function WebUSBConnect({ onConnect }) {
522607
return (
@@ -722,6 +807,11 @@ export default function Flash() {
722807
// Handle retry on error
723808
const handleRetry = () => window.location.reload()
724809

810+
// Render GPT dump diagnostic mode
811+
if (DUMP_GPT_MODE) {
812+
return <GptDumpMode qdlManager={qdlManager} imageManager={imageManager} />
813+
}
814+
725815
// Render landing page
726816
if (wizardScreen === 'landing' && !error) {
727817
return (

src/utils/manager.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import { getManifest } from './manifest'
55
import config from '../config'
66
import { createSteps, withProgress } from './progress'
77

8+
// Diagnostic mode - dump GPT partition info
9+
export const DUMP_GPT_MODE = new URLSearchParams(window.location.search).has('dump_gpt')
10+
811
// Fast mode for development - skips flashing system partition (the slowest)
912
// Enable with ?fast=1 in URL
1013
const FAST_MODE = new URLSearchParams(window.location.search).has('fast')
@@ -463,4 +466,85 @@ export class FlashManager {
463466
await this.#finalize()
464467
console.info(`Finalized in ${((performance.now() - start) / 1000).toFixed(2)}s`)
465468
}
469+
470+
/**
471+
* Diagnostic mode: connect and dump GPT from all LUNs
472+
* @returns {Promise<string|null>} Formatted GPT dump or null on error
473+
*/
474+
async dumpGpt() {
475+
this.#setStep(StepCode.CONNECTING)
476+
this.#setProgress(-1)
477+
478+
let usb
479+
try {
480+
usb = new usbClass()
481+
} catch (err) {
482+
console.error('[Flash] Connection error', err)
483+
this.#setError(ErrorCode.LOST_CONNECTION)
484+
return null
485+
}
486+
487+
try {
488+
await this.device.connect(usb)
489+
} catch (err) {
490+
if (err.name === 'NotFoundError') {
491+
console.info('[Flash] No device selected')
492+
this.#setStep(StepCode.READY)
493+
return null
494+
}
495+
console.error('[Flash] Connection error', err)
496+
this.#setError(ErrorCode.LOST_CONNECTION)
497+
return null
498+
}
499+
500+
this.#setConnected(true)
501+
this.#setMessage('Reading GPT...')
502+
503+
try {
504+
const lines = []
505+
lines.push('=== GPT Dump ===')
506+
lines.push(`Time: ${new Date().toISOString()}`)
507+
lines.push('')
508+
509+
// Get storage info
510+
const storageInfo = await this.device.getStorageInfo()
511+
lines.push('Storage Info:')
512+
lines.push(JSON.stringify(storageInfo, null, 2))
513+
lines.push('')
514+
515+
// Dump GPT from each LUN
516+
for (let lun = 0; lun < 6; lun++) {
517+
lines.push(`=== LUN ${lun} ===`)
518+
try {
519+
const gpt = await this.device.getGpt(lun)
520+
const partitions = gpt.getPartitions()
521+
522+
for (const part of partitions) {
523+
const isAB = part.name.endsWith('_a') || part.name.endsWith('_b')
524+
let line = `${part.name}: ${part.attributes}`
525+
526+
if (isAB) {
527+
// Parse A/B flags from attributes
528+
const attrs = BigInt(part.attributes)
529+
// Flags are at bits 48-55 (correct) or 54-61 (buggy)
530+
const flags48 = (attrs >> 48n) & 0xFFFFn
531+
const flags54 = (attrs >> 54n) & 0xFFFFn
532+
line += ` [bit48: 0x${flags48.toString(16)}, bit54: 0x${flags54.toString(16)}]`
533+
}
534+
lines.push(line)
535+
}
536+
} catch (err) {
537+
lines.push(`Error reading LUN ${lun}: ${err.message}`)
538+
}
539+
lines.push('')
540+
}
541+
542+
this.#setStep(StepCode.DONE)
543+
return lines.join('\n')
544+
} catch (err) {
545+
console.error('[Flash] Error dumping GPT', err)
546+
this.#setError(ErrorCode.UNKNOWN)
547+
return null
548+
}
549+
}
466550
}

0 commit comments

Comments
 (0)