edisonhello / dbux

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Introduction

This is a pre-alpha project, aiming at making the JS runtime and its dynamic call graph visual and interactive through a combination of instrumentation (using Babel) + a VSCode extension, effectively (ultimately) making it an amazing tool for (i) program comprehension + (ii) debugging.

The master branch is not quite active yet. Check out the dev branch instead.

Here is a (very very early, read: crude) 1min demo video of just a small subset of the features:

Development + Contributing: Getting Started

Prerequisites

  • node
  • vscode
  • yarn

Setup

git clone https://github.com/Domiii/dbux.git
cd dbux
code .
npm run dbux-install

# if dependencies bug out, run the (very aggressive) clean-up command: `npm run dbux-uninstall`

Start development

code . # open project in vscode
npm start # start webpack build of all projects in watch mode

Usage

  1. go to your debug tab, select dbux-code and press F5 (runs the vscode extension in debug mode)
  2. Inside of the new window, you can:
    • dbux-run # instruments + executes currently opened file
    • test on one of the pre-configured projects
    • use dbux-cli to setup + run your own project

Analyze with Python Notebooks

In the analyze/ folder, you find several python notebooks that allow you analyze the data that dbux generates. Here is how you set that up:

  1. Run some program with Dbux enabled (e.g. samples/[...]/oop1.js)
  2. In the VSCode extension, open a file of that program that has traces in it
  3. In VSCode Run Command (CTRL/Command + SHIFT + P) -> Dbux: Export file
  4. Make sure you have Python + Jupyter setup
    • Windows
      • Install Anaconda with chocolatey
      • Set your %PYTHONPATH% in system config to your Anaconda Lib + DLLs folders (e.g. C:\tools\Anaconda3\Lib;C:\tools\Anaconda3\DLLs;)
      • Done!
  5. Run one of the notebooks, load the file, and analyze :)

Test: Project 1

  1. After you opened a new VSCode window with dbux-code enabled (see steps above), in that window you can run + trace all kinds of code.
  2. Dbux currently has one frontend project pre-configured for testing purposes, that is todomvc's es6 version.
    • install it first: npm run p1-install
  3. Run it: npm run p1-start (starts webpack + webpack-dev-server)
  4. Open in browser (http://localhost:3030), then check results of the run in the extension test window

Architectural Notes

This is a multi-project monorepo.

Why is it not using LERNA? Because I did not know about LERNA when I started; but it's working quite well nevertheless :)

Some dependencies

Basics

`# jest` yarn add --dev jest jest-expect-message jest-extended
`# babel basics` yarn add --dev @babel/core @babel/cli @babel/node @babel/register 
`# babel plugins` yarn add --dev @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-decorators @babel/plugin-proposal-function-bind @babel/plugin-syntax-export-default-from @babel/plugin-syntax-dynamic-import @babel/plugin-transform-runtime && \
`# babel runtime` yarn add core-js@3 @babel/runtime
`# eslint` yarn add --dev eslint eslint-config-airbnb-base
`# webpack` yarn add --dev webpack webpack-cli webpack-dev-server nodemon
`# flow` yarn add --dev flow-bin @babel/preset-flow eslint-plugin-flowtype && npx flow init #&& npx flow
`# babel dev` yarn add --dev @babel/parser @babel/traverse @babel/types @babel/generator @babel/template @babel/code-frame babel-plugin-tester

or with npm:

`# jest` npm i -D jest jest-expect-message jest-extended
`# babel basics` npm i -D @babel/core @babel/cli @babel/node @babel/register 
`# babel plugins` npm i -D @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-decorators @babel/plugin-proposal-function-bind @babel/plugin-syntax-export-default-from @babel/plugin-syntax-dynamic-import @babel/plugin-transform-runtime && \
`# babel runtime` npm i -S core-js@3 @babel/runtime
`# eslint` npm i -D eslint eslint-config-airbnb-base

Upgrading Packages

`# babel` npm run dbux-install --force --save @babel/cli@latest @babel/core@latest @babel/node@latest @babel/plugin-proposal-class-properties@latest @babel/plugin-proposal-decorators@latest @babel/plugin-proposal-function-bind@latest @babel/plugin-proposal-optional-chaining@latest @babel/plugin-syntax-dynamic-import@latest @babel/plugin-syntax-export-default-from@latest @babel/plugin-syntax-flow@latest @babel/plugin-transform-runtime@latest @babel/preset-env@latest @babel/preset-flow@latest @babel/register@latest

`# babel instrumentation` @babel/code-frame@latest @babel/template@latest

package.json magic

  • replace: "([^"]+)": "([^"]+)",\n\s* w/ $1@latest

References

Debugging Intermediate + Advanced

References: AI-supported coding

References: babel + babel plugins

References: babel transpiler implementation details

References: npm

Reference: Istanbul + NYC

Istanbul + NYC add require hooks to instrument any loaded file on the fly

References: VSCode extensions

Some Notes on Implementation

(only very few of the features are explained here, a lot more to come in the future...)

dbux-data

  • Indexes
    • [shape] an index is a complete partitioning of all data of one particular collection
    • [storage method] all new data is categorized into all matching indexes
    • [storage invalidation] previously indexed data will generally never get evicted
    • [key type] (currently) keys of indices can only be numbers
      • TODO: add string keys as well, without reducing performance of number-based indices
    • [storage type] objects in indexes are always entries of Collections
  • Queries
    • [shape] usually we want Queries to be CachedQueries (currently all are) which perform an expensive computation and then store the result thereof
    • [storage method] only results of individual queries are cached when queried (not cached when data comes in)
    • [storage invalidation] cache will be invalidated when new data comes in (unless cfg.versionDependencies is empty)
    • [key type] the keys of cached query results are the input arguments ("args")
      • that's why args should ideally be a single primitive data type or a flat array of primitive data types
    • [storage type] queries can return and cache any data type

Known Issues

  • don't call process.exit (at least not immediately)
    • process.exit will kill the process before all recorded data has been sent out
    • if you MUST call it, make sure to put it in a setTimeout with 0.5-1s delay
    • NOTE: many frameworks that might kill your process allow disabling it (e.g. Mocha's --no-exit argument)
  • What applications work so well with DBUX?
    • TODO: we are still exploring that
  • What applications won't work so well with DBUX?
    • Proxies and custom object getters with side effects
      • For serialization dbux-runtime iterates (or will in the future iterate) over object properties
      • Thus possibly causing side effects with proxy and getter functions
      • At least it will leave unwanted traces (while attempting to "observe") - Damn you, Observer effect!!! :(
      • TODO: at least flag traces caused by dbux-runtime by setting some trace-triggered-from-dbux-builtin-call flag while running built-in functions
        • NOTE: This will still mess with proxy and getter functions that themselves have side effects, such as caching functions, tracers and more.
  • Issues under Windows
    • sometimes, when running things in VSCode built-in terminal, it might change to lower-case drive letter
      • This causes a mixture of lower-case and upper-case drive letters to start appearing in require paths
      • Official bug report: microsoft/vscode#9448
      • Solution: run command in external cmd or find a better behaving terminal

VSCode: Advanced Usage

General Tips&Tricks

Use VSCode as git diff tool

Keyboard shortcuts

NOTE: VSCode's Terminal has no "Clear" keybinding anymore You can re-add it manually:

  1. CTRL+SHIFT+P -> "Open Keyboard Shortcuts (JSON)"
  2. add (for Windows use ctrl; for MAC use cmd):
{ 
   "key": "ctrl+k",
   "command": "workbench.action.terminal.clear",
   "when": "terminalFocus"
},

Some of the more annoying problems that have already been resolved

Higher Order Questions

Questions that we can already answer

  • Which parts of my code executed?
  • How often did this code execute?
  • What did these expressions evaluate to during each execution?
  • What were the arguments passed to this function call?
  • Where did the execution go from here? Where did it come from?
  • Which events were triggered and how did its handlers execute?

TODO: Questions we want to work on next

  • Sub-graph filtering
    • Search sub graph contexts by keyword (QuickInput)
    • All traces/contexts/runs that referenced some object (ValueRef)

Future Work (even more cool questions)

  • Sub-graph filtering
    • Multiple filter UI modes
      • hide vs. grayed out?
  • What is the critical path in this sub-graph, in terms of call-stack depth?
    • NOTE: we don't aim to do performance analysis, so we can't find the actual critical path
  • Given two traces, find shortest path (or path that is most likely to be the actual path?)
    • TODO: Somehow visualize and allow interactions with that path
      • -> Possibly like a car navigation system -> listing all the twists and turns in a list
  • Interactive visualized call graph
    • zoom- and pan-able
    • multi-resolution
    • features and filters can be enabled and disabled
    • multiple coloring schemes (e.g. one each for color per file/context/feature type and more)11

Features

Data Recording + Data Processing Mechanisms

  • Instrumentation
  • Collection
  • Postprocessing
    • adding one-to-one fields (pre-index)
    • Index
    • adding one-to-one fields (post-index)
  • Query + CachedQuery

Concept: Contexts + StaticContexts

Concept: Traces + StaticTraces

Data Flow

Object Tracking

Error Reporting

Control Flow

Basic Control Flow

Callback Tracking

Interruptable functions: async

Interruptable functions: generator

Error reporting

  • Are dynamic vs. static exit traces of functions the same?
  • special attention: try statements

Call Graph Navigation

  • {Previous,Next}InContext
    • Use getTracesOfRealContext
    • [no_trace]
      • [Previous && current trace is Push && previous trace is Pop]
        • -> go
      • [Next && current trace is Pop && next trace is Push]
        • -> go
  • PreviousParent
    • First trace in current context --> context's parentTraceId
  • NextParent
    • Same as PreviousParent, but get "next in context" of parentTrace
  • PreviousChild
    • Use ParentTracesInRealContextIndex
  • NextChild
    • Same as PreviousChild, but use "next in context" of parentTrace

About

License:Apache License 2.0


Languages

Language:JavaScript 76.6%Language:Jupyter Notebook 21.6%Language:Python 0.9%Language:Shell 0.6%Language:CSS 0.3%Language:HTML 0.1%