Last active
September 26, 2025 13:52
-
-
Save Shreyas-Penkar/d6c02b6c589ee1332832dde8626baee3 to your computer and use it in GitHub Desktop.
Pretty UI for Timeline Graph
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Race Condition Timeline</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| body { | |
| font-family: 'Roboto Mono', monospace; | |
| background-color: #111827; | |
| color: #F3F4F6; | |
| } | |
| .timeline-container::before { | |
| content: ''; | |
| position: absolute; | |
| left: 50%; | |
| top: 0; | |
| bottom: 0; | |
| width: 2px; | |
| background-color: #4B5563; | |
| transform: translateX(-1px); | |
| } | |
| .event { | |
| position: relative; | |
| padding-left: 2rem; | |
| padding-right: 1rem; | |
| margin-bottom: 1rem; | |
| border-left: 2px solid #6B7280; | |
| } | |
| .event::before { | |
| content: ''; | |
| position: absolute; | |
| left: -6px; | |
| top: 12px; | |
| width: 10px; | |
| height: 10px; | |
| border-radius: 50%; | |
| background-color: white; | |
| border: 2px solid #111827; | |
| } | |
| .event-a::before { background-color: #3B82F6; } | |
| .event-b::before { background-color: #F59E0B; } | |
| .event-danger::before { background-color: #EF4444; } | |
| </style> | |
| </head> | |
| <body class="p-4 md:p-8"> | |
| <div class="max-w-7xl mx-auto"> | |
| <h1 class="text-2xl md:text-3xl font-bold text-center mb-4">Race Condition</h1> | |
| <p class="text-center text-gray-400 mb-8">Visualizing a Bug in form of Timeline.</p> | |
| <div class="flex justify-center space-x-6 mb-12"> | |
| <div class="flex items-center"> | |
| <span class="w-4 h-4 rounded-full bg-blue-500 mr-2"></span> | |
| <span>Thread A </span> | |
| </div> | |
| <div class="flex items-center"> | |
| <span class="w-4 h-4 rounded-full bg-amber-500 mr-2"></span> | |
| <span>Thread B </span> | |
| </div> | |
| <div class="flex items-center"> | |
| <span class="w-4 h-4 rounded-full bg-red-500 mr-2"></span> | |
| <span>Critical Event / Error</span> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-x-8 relative timeline-container"> | |
| <!-- Thread A Column --> | |
| <div id="threadA"> | |
| <h2 class="text-xl font-semibold text-blue-400 sticky top-4">Thread A</h2> | |
| </div> | |
| <!-- Thread B Column --> | |
| <div id="threadB"> | |
| <h2 class="text-xl font-semibold text-amber-400 sticky top-4">Thread B</h2> | |
| </div> | |
| <!-- Race Window --> | |
| <div id="race-window" class="absolute left-0 right-0 border-l-4 border-r-4 border-red-500/50 bg-red-500/10 rounded-lg pointer-events-none"> | |
| <div class="absolute -top-3 left-1/2 -translate-x-1/2 bg-red-500 text-white text-xs font-bold px-2 py-0.5 rounded-full"> | |
| RACE WINDOW | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const events = [ | |
| { thread: 'B', time: 0, desc: "Thread B starts" }, | |
| { thread: 'B', time: 100, desc: "Thread B creates first action" }, | |
| { thread: 'B', time: 5100, desc: "Thread B creates second action" }, | |
| { thread: 'B', time: 5150, desc: "❗ Context Invalidation" }, | |
| { thread: 'A', time: 5200, desc: "Thread A enters <code>function_name</code>" }, | |
| { thread: 'A', time: 5250, desc: "Thread A starts <code>function_name</code> – <strong>NO LOCK HELD</strong>" }, | |
| { thread: 'A', time: 5300, desc: "Thread A obtains pointer to <code>variable</code> in the list" }, | |
| { thread: 'A', time: 5350, desc: "⚡ Context switch occurs – Thread A paused" }, | |
| { thread: 'B', time: 5400, desc: "Thread B enters <code>function_name</code>, acquires <code>variable</code>" }, | |
| { thread: 'B', time: 5450, desc: "Thread B identifies action" }, | |
| { thread: 'B', time: 5500, desc: "❗ <code>function_name</code> and <code>function_name</code> executed, freeing the same <code>variable</code> held by Thread A", highlight: 'danger' }, | |
| { thread: 'B', time: 5550, desc: "Thread B releases <code>variable</code> and exits" }, | |
| { thread: 'A', time: 5600, desc: "⚡ Context switch back – Thread A resumes execution" }, | |
| { thread: 'A', time: 5650, desc: "🚨 USE-AFTER-FREE: Thread A accesses <code>variable</code> and <code>variable</code> on freed memory", highlight: 'error' } | |
| ]; | |
| const timelineSlots = {}; | |
| const threadAContainer = document.getElementById('threadA'); | |
| const threadBContainer = document.getElementById('threadB'); | |
| events.forEach(event => { | |
| const eventEl = document.createElement('div'); | |
| const timeKey = Math.floor(event.time / 10) * 10; | |
| if (!timelineSlots[timeKey]) { | |
| timelineSlots[timeKey] = { A: null, B: null }; | |
| } | |
| let eventClass = event.thread === 'A' ? 'event-a' : 'event-b'; | |
| let borderColor = event.thread === 'A' ? 'border-blue-500' : 'border-amber-500'; | |
| if (event.highlight === 'danger' || event.highlight === 'error') { | |
| eventClass = 'event-danger'; | |
| borderColor = 'border-red-500'; | |
| } | |
| eventEl.className = `event ${eventClass} transform transition-transform duration-300 hover:scale-105`; | |
| eventEl.style.marginLeft = event.thread === 'B' ? '-2px' : '0'; | |
| eventEl.innerHTML = ` | |
| <div class="flex items-baseline space-x-4"> | |
| <div class="font-bold text-gray-400 w-12 text-right">t=${event.time}</div> | |
| <div class="bg-gray-800 rounded-lg p-3 w-full border ${borderColor}/50 shadow-md"> | |
| <p class="text-gray-200 text-sm">${event.desc}</p> | |
| </div> | |
| </div> | |
| `; | |
| timelineSlots[timeKey][event.thread] = {el: eventEl, originalEvent: event}; | |
| }); | |
| const sortedTimes = Object.keys(timelineSlots).map(Number).sort((a,b) => a - b); | |
| sortedTimes.forEach(time => { | |
| const slot = timelineSlots[time]; | |
| if (slot.A) threadAContainer.appendChild(slot.A.el); | |
| else { | |
| const placeholder = document.createElement('div'); | |
| placeholder.className = 'h-20'; placeholder.style.visibility = 'hidden'; | |
| threadAContainer.appendChild(placeholder); | |
| } | |
| if (slot.B) threadBContainer.appendChild(slot.B.el); | |
| else { | |
| const placeholder = document.createElement('div'); | |
| placeholder.className = 'h-20'; placeholder.style.visibility = 'hidden'; | |
| threadBContainer.appendChild(placeholder); | |
| } | |
| }); | |
| function positionRaceWindow() { | |
| const raceWindowEl = document.getElementById('race-window'); | |
| // Start = event at t=250, End = event at t=450 // Control the Race Window extent here | |
| const startEl = timelineSlots[5350].A.el; | |
| const endEl = timelineSlots[5550].B.el; | |
| const startTop = startEl.offsetTop + startEl.offsetHeight/2; | |
| const endTop = endEl.offsetTop + endEl.offsetHeight/2; | |
| raceWindowEl.style.top = `${startTop}px`; | |
| raceWindowEl.style.height = `${endTop - startTop}px`; | |
| } | |
| window.addEventListener('load', positionRaceWindow); | |
| window.addEventListener('resize', positionRaceWindow); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment