Casestudy: Betere foutopsporing met DevTools

Een verbeterde foutopsporingservaring

De afgelopen maanden heeft het Chrome DevTools-team samengewerkt met het Angular-team om verbeteringen aan de foutopsporingservaring in Chrome DevTools te lanceren. Mensen uit beide teams werkten samen en ondernamen stappen om ontwikkelaars in staat te stellen webapplicaties te debuggen en te profileren vanuit het auteursperspectief : in termen van hun brontaal en projectstructuur, met toegang tot informatie die voor hen vertrouwd en relevant is.

Dit bericht neemt een kijkje onder de motorkap om te zien welke veranderingen in Angular en Chrome DevTools nodig waren om dit te bereiken. Hoewel sommige van deze veranderingen via Angular worden gedemonstreerd, kunnen ze ook op andere raamwerken worden toegepast. Het Chrome DevTools-team moedigt andere frameworks aan om de nieuwe console-API's en uitbreidingspunten voor de bronkaart over te nemen, zodat ook zij hun gebruikers een betere foutopsporingservaring kunnen bieden.

Negeerlijstcode

Bij het debuggen van applicaties met Chrome DevTools willen auteurs over het algemeen alleen hun code zien, en niet die van het onderliggende raamwerk of een afhankelijkheid die is weggestopt in de map node_modules .

Om dit te bereiken heeft het DevTools-team een ​​extensie voor bronkaarten geïntroduceerd, genaamd x_google_ignoreList . Deze extensie wordt gebruikt om bronnen van derden te identificeren, zoals raamwerkcode of door bundels gegenereerde code. Wanneer een framework deze extensie gebruikt, vermijden auteurs nu automatisch code die ze niet willen zien of doorlopen zonder dat ze dit vooraf handmatig hoeven te configureren .

In de praktijk kan Chrome DevTools automatisch code verbergen die als zodanig wordt geïdentificeerd in stacktraces, de bronstructuur en het Quick Open-dialoogvenster, en ook het stap- en hervattingsgedrag in de debugger verbeteren.

Een geanimeerde GIF met DevTools voor en na. Merk op hoe DevTools in de onderstaande afbeelding de geschreven code in de boomstructuur toont, niet langer een van de raamwerkbestanden voorstelt in het menu "Snel openen" en aan de rechterkant een veel schonere stapeltracering toont.

De x_google_ignoreList bronkaartextensie

In bronkaarten verwijst het nieuwe x_google_ignoreList veld naar de sources en vermeldt het de indices van alle bekende bronnen van derden in die bronkaart. Bij het parseren van de bronkaart gebruikt Chrome DevTools dit om erachter te komen welke delen van de code op de negeerlijst moeten worden geplaatst.

Hieronder vindt u een bronkaart voor een gegenereerd bestand out.js . Er zijn twee originele sources die hebben bijgedragen aan het genereren van het uitvoerbestand: foo.js en lib.js . Het eerste is iets dat een website-ontwikkelaar heeft geschreven en het laatste is een raamwerk dat ze hebben gebruikt.

{
  "version" : 3,
  "file": "out.js",
  "sourceRoot": "",
  "sources": ["foo.js", "lib.js"],
  "sourcesContent": ["...", "..."],
  "names": ["src", "maps", "are", "fun"],
  "mappings": "A,AAAB;;ABCDE;"
}

De sourcesContent is opgenomen voor beide originele bronnen en Chrome DevTools zou deze bestanden standaard weergeven in de Debugger:

  • Als bestanden in de Bronnenboom.
  • Als resultaat in het dialoogvenster Snel openen.
  • Zoals in kaart gebrachte oproepframelocaties in foutenstapelsporen terwijl gepauzeerd op een breekpunt en tijdens het stappen.

Er is nog een extra stukje informatie dat nu in bronkaarten kan worden opgenomen om te identificeren welke van die bronnen code van de eerste of derde partij is:

{
  ...
  "sources": ["foo.js", "lib.js"],
  "x_google_ignoreList": [1],
  ...
}

Het nieuwe x_google_ignoreList veld bevat een enkele index die verwijst naar de sources : 1. Dit specificeert dat de regio's die zijn toegewezen aan lib.js in feite code van derden zijn die automatisch aan de negeerlijst moet worden toegevoegd.

In een complexer voorbeeld, hieronder weergegeven, specificeren de indices 2, 4 en 5 dat de regio's die zijn toegewezen aan lib1.ts , lib2.coffee en hmr.js allemaal code van derden zijn die automatisch aan de negeerlijst moet worden toegevoegd.

{
  ...
  "sources": ["foo.html", "bar.css", "lib1.ts", "baz.js", "lib2.coffee", "hmr.js"],
  "x_google_ignoreList": [2, 4, 5],
  ...
}

Als u een framework- of bundelontwikkelaar bent, zorg er dan voor dat de bronkaarten die tijdens het bouwproces worden gegenereerd, dit veld bevatten, zodat u gebruik kunt maken van deze nieuwe mogelijkheden in Chrome DevTools.

x_google_ignoreList in Angular

Vanaf Angular v14.1.0 is de inhoud van de node_modules en webpack mappen gemarkeerd als “te negeren” .

Dit werd bereikt door een verandering in angular-cli door een plug-in te maken die aansluit op de Compiler module van webpack

De webpack-plug-in die onze technici hebben gemaakt, haakt in op de PROCESS_ASSETS_STAGE_DEV_TOOLING -fase en vult het veld x_google_ignoreList in de bronkaarten in voor de uiteindelijke assets die webpack genereert en de browser wordt geladen.

const map = JSON.parse(mapContent) as SourceMap;
const ignoreList = [];

for (const [index, path] of map.sources.entries()) {
  if (path.includes('/node_modules/') || path.startsWith('webpack/')) {
    ignoreList.push(index);
  }
}

map[`x_google_ignoreList`] = ignoreList;
compilation.updateAsset(name, new RawSource(JSON.stringify(map)));

Gekoppelde stapelsporen

Stack-traces beantwoorden de vraag "hoe ben ik hier terechtgekomen" , maar vaak is dit vanuit het perspectief van de machine, en niet noodzakelijkerwijs iets dat overeenkomt met het perspectief van de ontwikkelaar of hun mentale model van de runtime van de applicatie. Dit is vooral het geval als bepaalde bewerkingen gepland zijn om later asynchroon te gebeuren: het kan nog steeds interessant zijn om de “hoofdoorzaak” of de planningskant van dergelijke bewerkingen te kennen, maar dat is precies iets dat geen deel zal uitmaken van een asynchrone stacktrace.

V8 heeft intern een mechanisme om dergelijke asynchrone taken bij te houden wanneer standaard browserplanningsprimitieven worden gebruikt, zoals setTimeout . In die gevallen gebeurt dit standaard, zodat de ontwikkelaars deze al kunnen inspecteren! Maar bij complexere projecten is dat niet zo eenvoudig, vooral als je een raamwerk gebruikt met geavanceerdere planningsmechanismen, bijvoorbeeld een raamwerk dat zonetracering uitvoert, aangepaste taken in de wachtrij plaatst of updates opsplitst in verschillende werkeenheden die worden overlopen. tijd.

Om dit aan te pakken, heeft DevTools een mechanisme op het console geïntroduceerd dat de “Async Stack Tagging API” wordt genoemd. Hierdoor kunnen framework-ontwikkelaars hints geven over de locaties waar bewerkingen worden gepland en waar deze bewerkingen worden uitgevoerd.

De Asynchrone Stack Tagging-API

Zonder Async Stack Tagging verschijnen stapelsporen voor code die op complexe manieren asynchroon wordt uitgevoerd door frameworks, zonder verbinding met de code waarop deze was gepland.

Een stacktrace van asynchroon uitgevoerde code zonder informatie over wanneer deze was gepland. Het toont alleen de stacktracering vanaf `requestAnimationFrame`, maar bevat geen informatie over het tijdstip waarop deze was gepland.

Met Async Stack Tagging is het mogelijk om deze context te bieden, en de stacktrace ziet er als volgt uit:

Een stacktrace van asynchroon uitgevoerde code met informatie over wanneer deze was gepland. Merk op hoe het, in tegenstelling tot voorheen, `businessLogic` en `schedule` in de stacktrace bevat.

Om dit te bereiken, gebruikt u een nieuwe console met de naam console.createTask() die de Async Stack Tagging API biedt. De handtekening luidt als volgt:

interface Console {
  createTask(name: string): Task;
}

interface Task {
  run<T>(f: () => T): T;
}

Het aanroepen van console.createTask() retourneert een Task instantie die u later kunt gebruiken om de asynchrone code uit te voeren.

// Task Creation
const task = console.createTask(name);

// Task Execution
task.run(f);

De asynchrone bewerkingen kunnen ook worden genest, waarbij de “hoofdoorzaken” achtereenvolgens in de stacktrace worden weergegeven.

Taken kunnen een onbeperkt aantal keren worden uitgevoerd en de werklast kan per uitvoering verschillen. De call-stack op de planningssite wordt onthouden totdat het taakobject is verzameld.

De Async Stack Tagging-API in Angular

In Angular zijn wijzigingen aangebracht in NgZone , de uitvoeringscontext van Angular die blijft bestaan ​​bij asynchrone taken.

Bij het plannen van een taak wordt console.createTask() gebruikt, indien beschikbaar. Het resulterende Task wordt opgeslagen voor verder gebruik. Bij het aanroepen van de taak zal NgZone de opgeslagen Task gebruiken om deze uit te voeren.

Deze wijzigingen zijn in NgZone 0.11.8 van Angular terechtgekomen via pull-aanvragen #46693 en #46958 .

Vriendelijke oproepframes

Frameworks genereren bij het bouwen van een project vaak code uit allerlei sjabloontalen, zoals Angular- of JSX-sjablonen die HTML-achtige code omzetten in gewoon JavaScript dat uiteindelijk in de browser draait. Soms krijgen dit soort gegenereerde functies namen die niet erg vriendelijk zijn: namen met één letter nadat ze zijn verkleind, of obscure of onbekende namen, zelfs als dat niet het geval is.

In Angular is het niet ongebruikelijk om oproepframes met namen als AppComponent_Template_app_button_handleClick_1_listener in stacktraces te zien.

Schermafbeelding van stacktrace met een automatisch gegenereerde functienaam.

Om dit aan te pakken ondersteunt Chrome DevTools nu het hernoemen van deze functies via bronkaarten. Als een brontoewijzing een naaminvoer heeft voor het begin van een functiebereik (dat wil zeggen de linkerbovenliggende parameter van de parameterlijst), moet het aanroepframe die naam weergeven in de stacktrace.

Vriendelijke oproepframes in hoekig

Het hernoemen van oproepframes in Angular is een voortdurende inspanning. We verwachten dat deze verbeteringen in de loop van de tijd geleidelijk zullen plaatsvinden.

Tijdens het parseren van de HTML-sjablonen die auteurs hebben geschreven, genereert de Angular-compiler TypeScript-code, die uiteindelijk wordt omgezet in JavaScript-code die de browser laadt en uitvoert.

Als onderdeel van dit codegeneratieproces worden ook bronkaarten gemaakt. We onderzoeken momenteel manieren om functienamen op te nemen in het veld 'namen' van brontoewijzingen, en om naar die namen te verwijzen in de toewijzingen tussen de gegenereerde code en de originele code.

Als er bijvoorbeeld een functie voor een gebeurtenislistener wordt gegenereerd en de naam ervan is onvriendelijk of verwijderd tijdens het verkleinen, kunnen brontoewijzingen nu de meer gebruiksvriendelijke naam voor deze functie opnemen in het veld 'namen' en de toewijzing voor het begin van de functie scope kan nu naar deze naam verwijzen (dat wil zeggen, het linker bovenliggende item van de parameterlijst). Chrome DevTools gebruikt deze namen vervolgens om de oproepframes in stacktraces te hernoemen.

Vooruit kijken

Het was een geweldige ervaring om Angular als testpiloot te gebruiken om ons werk te verifiëren. We horen graag van framework-ontwikkelaars en geven ons feedback over deze uitbreidingspunten .

Er zijn nog meer gebieden die we graag willen verkennen. In het bijzonder hoe u de profileringservaring in DevTools kunt verbeteren.