├── README.md ├── bios ├── COPYING.LESSER └── seabios.bin ├── build ├── LICENSE ├── libv86.js └── v86.wasm ├── images ├── gdb-avr-10.1-bzImage.bin ├── gdb-multiarch-10.1-bzImage.bin ├── gdb-multiarch-10.1-python-bzImage-licenses.tar.gz ├── gdb-multiarch-10.1-python-bzImage.bin ├── gdb-multiarch-10.2-bzImage.bin ├── gdb-multiarch-11.2-bzImage.bin └── licenses.tar.gz ├── index.html └── src ├── main.js ├── style.css └── worker.js /README.md: -------------------------------------------------------------------------------- 1 | # Web GDB 2 | 3 | GDB (multiarch) running in a web-browser! 4 | 5 | > [Learn how to use Web GDB on Wokwi.com](https://docs.wokwi.com/gdb-debugging) 6 | 7 | ## How can I use it? 8 | 9 | To use Web GDB on Wokwi, open any project (e.g. this [Simon game](https://wokwi.com/arduino/libraries/demo/simon-game)), 10 | click on the code editor, and press F1. In the prompt that opens, type "GDB": 11 | 12 | ![Wokwi Web GDB](https://blog.wokwi.com/content/images/2021/02/image-8.png) 13 | 14 | Choose the "debug build" option (the release build is harder to debug, but it's useful if your program uses the FastLED library). 15 | Web GDB will load in a new browser tab (you have to be a bit patient), and you should get the familiar GDB prompt: 16 | 17 | ``` 18 | 0x00000000 in __vectors () 19 | (gdb) 20 | ``` 21 | 22 | At this point, you can write `continue` to start the program, or better - check out the 23 | [Arduino/AVR GDB Cheatsheet](https://blog.wokwi.com/gdb-avr-arduino-cheatsheet/) to see all the things GDB can do for you! 24 | 25 | A live version of Web GDB is [hosted on GitHub pages](https://wokwi.github.io/web-gdb/). 26 | 27 | ## How does it work? 28 | 29 | Great question, you'll find the answer in my [Running GDB in the Browser](https://blog.wokwi.com/running-gdb-in-the-browser) blog post. 30 | -------------------------------------------------------------------------------- /bios/COPYING.LESSER: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /bios/seabios.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wokwi/web-gdb/c7c9416890b2088412c86698d32652de5c0aa544/bios/seabios.bin -------------------------------------------------------------------------------- /build/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, The v86 contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /build/v86.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wokwi/web-gdb/c7c9416890b2088412c86698d32652de5c0aa544/build/v86.wasm -------------------------------------------------------------------------------- /images/gdb-avr-10.1-bzImage.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wokwi/web-gdb/c7c9416890b2088412c86698d32652de5c0aa544/images/gdb-avr-10.1-bzImage.bin -------------------------------------------------------------------------------- /images/gdb-multiarch-10.1-bzImage.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wokwi/web-gdb/c7c9416890b2088412c86698d32652de5c0aa544/images/gdb-multiarch-10.1-bzImage.bin -------------------------------------------------------------------------------- /images/gdb-multiarch-10.1-python-bzImage-licenses.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wokwi/web-gdb/c7c9416890b2088412c86698d32652de5c0aa544/images/gdb-multiarch-10.1-python-bzImage-licenses.tar.gz -------------------------------------------------------------------------------- /images/gdb-multiarch-10.1-python-bzImage.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wokwi/web-gdb/c7c9416890b2088412c86698d32652de5c0aa544/images/gdb-multiarch-10.1-python-bzImage.bin -------------------------------------------------------------------------------- /images/gdb-multiarch-10.2-bzImage.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wokwi/web-gdb/c7c9416890b2088412c86698d32652de5c0aa544/images/gdb-multiarch-10.2-bzImage.bin -------------------------------------------------------------------------------- /images/gdb-multiarch-11.2-bzImage.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wokwi/web-gdb/c7c9416890b2088412c86698d32652de5c0aa544/images/gdb-multiarch-11.2-bzImage.bin -------------------------------------------------------------------------------- /images/licenses.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wokwi/web-gdb/c7c9416890b2088412c86698d32652de5c0aa544/images/licenses.tar.gz -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Web GDB Multiarch 8 | 9 | 10 | 11 | 12 |
13 |

Wokwi Web GDB

14 |
15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | const worker = new Worker('src/worker.js' + location.search); 2 | 3 | const term = new Terminal({ 4 | cursorBlink: true, 5 | logLevel: 'off', 6 | }); 7 | const fitAddon = new FitAddon.FitAddon(); 8 | term.loadAddon(fitAddon); 9 | term.open(document.querySelector('#terminal')); 10 | fitAddon.fit(); 11 | window.addEventListener('resize', () => { 12 | fitAddon.fit(); 13 | }); 14 | 15 | term.write('Preparing your online GDB session...\r\n'); 16 | 17 | const pipe = new MessageChannel(); 18 | worker.postMessage({ type: 'init', data: pipe.port1 }, [pipe.port1]); 19 | if (window.opener) { 20 | window.opener.postMessage({ type: 'gdbInit', data: pipe.port2 }, '*', [pipe.port2]); 21 | } 22 | 23 | term.onData((data) => { 24 | worker.postMessage({ type: 'serial', data }); 25 | }); 26 | 27 | worker.addEventListener('message', (event) => { 28 | const msg = event.data; 29 | if (msg.type === 'serial') { 30 | term.write(msg.data); 31 | } 32 | if (msg.type === 'progress') { 33 | term.write(`\x1b[1;33m${msg.data}\x1b[0m\r\n`); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html, 6 | body { 7 | margin: 0; 8 | padding: 0; 9 | height: 100%; 10 | } 11 | 12 | body { 13 | background: black; 14 | display: flex; 15 | flex-direction: column; 16 | color: #ccc; 17 | font-family: Arial, Helvetica, sans-serif; 18 | } 19 | 20 | header { 21 | background: #333; 22 | color: white; 23 | font-weight: 600; 24 | padding: 8px; 25 | } 26 | 27 | header h1 { 28 | margin: 0; 29 | font-size: 18px; 30 | } 31 | 32 | main { 33 | flex: 1; 34 | text-align: center; 35 | padding: 8px 0 0 8px; 36 | } 37 | 38 | #terminal { 39 | display: inline-block; 40 | height: 100%; 41 | width: 100%; 42 | } 43 | -------------------------------------------------------------------------------- /src/worker.js: -------------------------------------------------------------------------------- 1 | const selfUrl = new URL(location.href); 2 | const arch = selfUrl.searchParams.get('arch') === 'avr' ? 'avr' : 'multiarch'; 3 | const python = selfUrl.searchParams.get('python') === '1'; 4 | const version = (python || arch == 'avr') ? '10.1' : '11.2' 5 | const suffix = python ? `-python` : ''; 6 | const imageName = `gdb-${arch}-${version}${suffix}-bzImage.bin`; 7 | 8 | importScripts('../build/libv86.js?v=2'); 9 | 10 | console.log('Worker starting...'); 11 | console.log(`Loading system from ${imageName}`); 12 | 13 | const gdb_sh = ` 14 | #!/bin/sh 15 | while true; do 16 | eval \`resize\` 17 | gdb -ex "dir /mnt" -ex "file /mnt/sketch.elf" -ex "target remote /dev/ttyS1" 18 | done 19 | `; 20 | 21 | class GDBServer { 22 | constructor(messagePort) { 23 | this.messagePort = messagePort; 24 | this.elf = null; 25 | this.sources = null; 26 | this.onResponse = null; 27 | this.onELFUpdated = null; 28 | this.gdbBuf = ''; 29 | messagePort.onmessage = (e) => { 30 | const msg = e.data; 31 | switch (msg.type) { 32 | case 'gdb': 33 | if (this.onResponse) { 34 | this.onResponse(msg.data); 35 | } 36 | break; 37 | case 'elf': 38 | this.elf = msg.data; 39 | if (this.onELFUpdated) { 40 | this.onELFUpdated(); 41 | } 42 | break; 43 | case 'sources': 44 | this.sources = msg.data; 45 | if (this.onELFUpdated) { 46 | this.onELFUpdated(); 47 | } 48 | break; 49 | } 50 | }; 51 | } 52 | 53 | requestElf() { 54 | const { messagePort } = this; 55 | messagePort.postMessage({ type: 'downloadSources' }); 56 | messagePort.postMessage({ type: 'downloadElf' }); 57 | } 58 | 59 | send(chr) { 60 | const { messagePort } = this; 61 | // Handle break 62 | if (messagePort && chr === '\x03') { 63 | console.log('BREAK'); 64 | messagePort.postMessage({ type: 'break' }); 65 | return; 66 | } 67 | 68 | this.gdbBuf += chr; 69 | for (;;) { 70 | const dolla = this.gdbBuf.indexOf('$'); 71 | const hash = this.gdbBuf.indexOf('#', dolla + 1); 72 | if (dolla < 0 || hash < 0 || hash + 2 > this.gdbBuf.length) { 73 | return; 74 | } 75 | const cmd = this.gdbBuf.substr(dolla + 1, hash - dolla - 1); 76 | this.gdbBuf = this.gdbBuf.substr(hash + 2); 77 | if (messagePort) { 78 | if (this.onResponse) { 79 | this.onResponse('+'); 80 | } 81 | messagePort.postMessage({ type: 'gdb', data: cmd }); 82 | } 83 | } 84 | } 85 | } 86 | 87 | class GDBRunner { 88 | constructor() { 89 | this.settings = { 90 | wasm_path: '../build/v86.wasm', 91 | bios: { url: '../bios/seabios.bin' }, 92 | bzimage: { url: `../images/${imageName}` }, 93 | cmdline: 'tsc=reliable mitigations=off random.trust_cpu=on', 94 | autostart: false, 95 | disable_speaker: true, 96 | memory_size: (python ? 96 : 64) * 1024 * 1024, 97 | vga_memory_size: 2 * 1024 * 1024, 98 | filesystem: {}, 99 | uart1: true, 100 | }; 101 | this.cache = null; 102 | this.cachedData = null; 103 | this.ready = false; 104 | this.slashFound = false; 105 | this.gdbServer = null; 106 | this.cacheSaved = false; 107 | this.bootMessageDisplayed = false; 108 | this.emulator = null; 109 | } 110 | 111 | reportProgress(message) { 112 | postMessage({ type: 'progress', data: message }); 113 | } 114 | 115 | async init() { 116 | const { settings } = this; 117 | this.cache = typeof caches !== 'undefined' ? await caches.open('gdb-state-v3') : null; 118 | this.cachedData = this.cache ? await this.cache.match(imageName) : null; 119 | if (this.cachedData) { 120 | this.reportProgress('✅ System loaded from cache'); 121 | delete settings.bios; 122 | delete settings.bzimage; 123 | this.cacheSaved = true; 124 | this.bootMessageDisplayed = true; 125 | } 126 | const emulator = new V86Starter(settings); 127 | emulator.add_listener('serial0-output-char', this.onSerial0Output); 128 | emulator.add_listener('serial1-output-char', this.onSerial1Output); 129 | emulator.add_listener('emulator-loaded', this.onEmulatorLoaded); 130 | emulator.add_listener('emulator-ready', this.onEmulatorReady); 131 | 132 | this.emulator = emulator; 133 | } 134 | 135 | loadElf() { 136 | const { gdbServer, emulator } = this; 137 | const { elf, sources } = this.gdbServer; 138 | if (!elf && gdbServer) { 139 | gdbServer.requestElf(); 140 | return; 141 | } 142 | if (elf && this.ready) { 143 | const data = Uint8Array.from(atob(elf), (c) => c.charCodeAt(0)); 144 | emulator.create_file('/sketch.elf', data); 145 | if (sources) { 146 | for (const [path, content] of sources) { 147 | const pathParts = path.split('/'); 148 | const filename = pathParts[pathParts.length - 1]; 149 | emulator.create_file(filename, new TextEncoder().encode(content)); 150 | } 151 | } 152 | } 153 | } 154 | 155 | attachGDBServer(gdbServer) { 156 | this.gdbServer = gdbServer; 157 | gdbServer.onELFUpdated = () => { 158 | this.loadElf(); 159 | }; 160 | gdbServer.onResponse = (response) => { 161 | this.serial1Write(response); 162 | }; 163 | } 164 | 165 | startGDB() { 166 | this.emulator.serial0_send('. /mnt/gdb.sh\n'); 167 | } 168 | 169 | input(chars) { 170 | this.emulator.serial0_send(chars); 171 | } 172 | 173 | async cacheState() { 174 | if (this.cacheSaved) { 175 | return; 176 | } 177 | const frozenState = await new Promise((resolve, reject) => 178 | this.emulator.save_state((err, result) => (err != null ? reject(err) : resolve(result))) 179 | ); 180 | if (!this.cache) { 181 | return; 182 | } 183 | this.cacheSaved = true; 184 | this.cache 185 | .put( 186 | imageName, 187 | new Response(frozenState, { 188 | headers: { 'Content-type': 'application/binary' }, 189 | }) 190 | ) 191 | .catch(console.error); 192 | console.log('emulator: state saved to cache.'); 193 | } 194 | 195 | onEmulatorLoaded = async () => { 196 | if (this.cachedData) { 197 | this.reportProgress('✅ Emulator initialized'); 198 | this.emulator.restore_state(await this.cachedData.arrayBuffer()); 199 | this.slashFound = true; 200 | this.ready = true; 201 | setTimeout(() => { 202 | this.loadElf(); 203 | this.startGDB(); 204 | }, 0); 205 | } else { 206 | this.reportProgress( 207 | '👷‍♀️ Installing GDB... This only happens once and can take up to 1 minute.' 208 | ); 209 | } 210 | this.emulator.run(); 211 | this.emulator.create_file('gdb.sh', new TextEncoder().encode(gdb_sh)); 212 | }; 213 | 214 | onEmulatorReady = () => { 215 | console.log('emulator: ready'); 216 | this.gdbServer.requestElf(); 217 | }; 218 | 219 | onSerial0Output = async (chr) => { 220 | if (!this.bootMessageDisplayed) { 221 | this.reportProgress('✅ System booting...'); 222 | this.bootMessageDisplayed = true; 223 | } 224 | if (chr === '/' && !this.ready) { 225 | if (this.slashFound) { 226 | await this.cacheState(); 227 | this.ready = true; 228 | for (const char of '\r\n') { 229 | self.postMessage({ type: 'serial', data: char }); 230 | } 231 | this.startGDB(); 232 | } 233 | this.loadElf(); 234 | this.slashFound = true; 235 | } 236 | if (!this.ready) { 237 | return; 238 | } 239 | self.postMessage({ type: 'serial', data: chr }); 240 | }; 241 | 242 | serial1Write(msg) { 243 | this.emulator.serial_send_bytes( 244 | 1, 245 | msg.split('').map((ch) => ch.charCodeAt(0)) 246 | ); 247 | } 248 | 249 | onSerial1Output = (chr) => { 250 | if (this.ready) { 251 | this.gdbServer.send(chr); 252 | } 253 | }; 254 | } 255 | 256 | const runner = new GDBRunner(); 257 | onmessage = (e) => { 258 | const msg = e.data; 259 | if (msg.type === 'init') { 260 | runner.attachGDBServer(new GDBServer(msg.data)); 261 | runner.init().catch(console.error); 262 | console.log('GDBServer ready!'); 263 | } 264 | if (msg.type === 'serial') { 265 | runner.input(msg.data); 266 | } 267 | }; 268 | --------------------------------------------------------------------------------