nodejs / llnode Goto Github PK
View Code? Open in Web Editor NEWAn lldb plugin for Node.js and V8, which enables inspection of JavaScript states for insights into Node.js processes and their core dumps.
License: Other
An lldb plugin for Node.js and V8, which enables inspection of JavaScript states for insights into Node.js processes and their core dumps.
License: Other
llnode: v1.2.0
OS X: 10.10.5
uname -a
Darwin Jeremiahs-MacBook-Pro.local 14.5.0 Darwin Kernel Version 14.5.0: Sun Sep 25 22:07:15 PDT 2016; root:xnu-2782.50.9~1/RELEASE_X86_64 x86_64
lldb -v
lldb-340.4.119
XCode version: 7.2 (7C68)
I'd like to set up tests running llnode against coredumps. I've noticed that the current suite only tests attaching to a running process, and I'd like to extend it to run the same tests on an aborted process (I think the simplest method would be to run with --abort-on-uncaught-exception
and then run the existing suite on the dump).
Does anyone have any views on how this should look (or how this should be implemented)?
>
lldb ../node/node mock-http-server.js
(lldb) target create "../node/node"
Current executable set to '../node/node' (x86_64).
(lldb) settings set -- target.run-args "mock-http-server.js"
(lldb) r
Process 4374 launched: '../node/node' (x86_64)
/Users/Jeremiah/Documents/node/out/Release/node[4374]: ../src/async-wrap.cc:115:void node::SetupHooks(const FunctionCallbackInfo<v8::Value> &): Assertion `env->async_hooks_init_function().IsEmpty()' failed.
1: node::Abort() [/Users/Jeremiah/Documents/here-be-dragons/../node/node]
2: node::RunMicrotasks(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/Jeremiah/Documents/here-be-dragons/../node/node]
3: node::SetupHooks(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/Jeremiah/Documents/here-be-dragons/../node/node]
4: v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) [/Users/Jeremiah/Documents/here-be-dragons/../node/node]
5: v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::(anonymous namespace)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>) [/Users/Jeremiah/Documents/here-be-dragons/../node/node]
6: v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [/Users/Jeremiah/Documents/here-be-dragons/../node/node]
7: 0x2dba51c092a7
Process 4374 stopped
* thread #1: tid = 0x612c, 0x00007fff86137286 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00007fff86137286 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
-> 0x7fff86137286 <+10>: jae 0x7fff86137290 ; <+20>
0x7fff86137288 <+12>: movq %rax, %rdi
0x7fff8613728b <+15>: jmp 0x7fff86132c53 ; cerror_nocancel
0x7fff86137290 <+20>: retq
(lldb) v8 bt
(lldb)
ping @indutny ?
I suspect we've hit this known and fixed bug with lldb 3.8:
With a live core dump, lldb hangs:
# lldb -f /usr/bin/node -c /var/cores/core.node.30833
(lldb) target create "/usr/bin/node" --core "/var/cores/core.node.30833"
[...hang...]
Could the install instructions for Linux note this and the fix? Would the current fix be an lldb build from source, or is it packaged with the patch yet in lldb 3.8 or 3.9?
Many of lldb errors are left unchecked in the source code, all of them should be checked for the failure. Proper error reporting should be in place.
I thought I’d open this issue to discuss whether Windows support for LLNode is something anyone wants.
I’ve got and LLDB build up and running on Windows so I had a chance to try building LLNode on Windows. Running gyp_llnode worked fairly well.
The resulting Visual Studio project created by gyp required a little tweaking (mostly things I could google for). The main issue was that LLNode uses the getopt library which isn’t on Windows by default. Given that the getopt problem could probably be fixed running LLNode on Windows looks do-able but would need a bit of work and would probably break fairly quickly unless someone was using it regularly.
There’s two reasons why someone might want to do this:
I’m not sure how important the first scenario is but the second one might be useful to developers with Windows machines who deploy to Linux. Of course you do have to get LLDB for Windows, probably by building it yourself, which isn’t hard but it is quite time consuming. (Developers with Mac’s who deploy to Linux are effectively already supported.)
Any thoughts? Does anyone else have a strong need for this? (I’m using a Mac so I’m neutral.)
Would anyone mind if I'll do a release, and add it to homebrew? Given that node v8 was just released, it seems beneficial to publish the version that supports it (thanks to @bnoordhuis )
Hi!
The current llnode README documentation states that the lldb version to install is 3.8
. Well... I copy/pasted the README installation instructions, but all I got when trying to load a coredump is another coredump from lldb.
The coredump I try to load is generated by a process running either under node 4.6.1 or 6.9.1 (I tried both):
$ lldb-3.8 node -c core
(lldb) target create "node" --core "core"
Segmentation fault (core dumped)
Same thing happens with:
Note that gdb has no problem loading my coredumps files. Of course I can't do much with gdb. :-)
It's when I stumbled upon this PR that I realized that you yourselves use lldb 3.6. So I tried that version, and now everything work fine.
This issue is more about the README documentation than anything but, if needs be, I will be happy to provide any additional information you need to pinpoint where the problem is.
had to add:
#include <string.h>
to src/llnode.cc after code list change.
Hi - Just taking this for a drive on FreeBSD 11.0-RELEASE
Build went smoothly following a tweaked set of the linux instructions
* Clone this repo
# git clone https://github.com/nodejs/llnode.git && cd llnode
* Install llvm with lldb pieces
# pkg install llvm40
# rm -fr /usr/bin/lldb
# ln -s /usr/local/bin/lldb40 /usr/bin/lldb
* Initialize GYP
# git clone https://chromium.googlesource.com/external/gyp.git tools/gyp
* Configure
# ./gyp_llnode -Dlldb_dir=/usr/local/llvm40/
* Build
# gmake -C out/ -j9
Anyway when I follow this tutorial https://developer.ibm.com/node/2016/08/15/exploring-node-js-core-dumps-using-the-llnode-plugin-for-lldb/ (thanks @hhellyer) I get a segmentation fault (core dump) when I try the v8 source list
command after selecting the frame.
Interestingly I down graded my llvm to 3.9 and everything worked as expected so the issue does seem to be introduced with 4.0.
# uname -a
FreeBSD 11.0-RELEASE-p9 FreeBSD 11.0-RELEASE-p9 #0: Tue Apr 11 08:48:40 UTC 2017
[email protected]:/usr/obj/usr/src/sys/GENERIC amd64
# lldb -v
lldb version 4.0.0
The one that works is
# uname -a
FreeBSD 11.0-RELEASE-p9 FreeBSD 11.0-RELEASE-p9 #0: Tue Apr 11 08:48:40 UTC 2017
[email protected]:/usr/obj/usr/src/sys/GENERIC amd64
# lldb -v
lldb version 3.9.1 ( revision )
I tried loading the core dump lldb lldb -c lldb-4.0.0.core
but I got error: '/usr/bin/lldb' doesn't contain any 'host' platform architectures: x86_64, i386
and don't know how to get any further with diagnosing this but as it's a regression I thought I had better log something.
Xcode recently updated lldb on my system. Now lldb reports:
$ lldb -v
lldb-370.0.40
Swift-3.1
However this doesn't match up to the actual lldb levels so I forced the installation script to use 3.9 headers. This built and as a result I was able to run v8 findjsobjects without needing to run one of the scripts to generate memory regions.
This simplifies the usage of llnode a lot so I will update the npm install scripts. Ideally I need to find a table that maps Xcode versions to 'real' lldb releases from the lldb git repository.
$ ./gyp_llnode -Dlldb_dir=/usr/lib/llvm-3.9/
Traceback (most recent call last):
File "./gyp_llnode", line 85, in <module>
(major, minor), is_clang = compiler_version()
ValueError: need more than 1 value to unpack
How to solve it ? My code has been updated.
It makes sense to cut off strings when executing v8 inspect <string address>
since most strings are very long, but I'd like to be able to override this via an option.
In some cases (like when looking at a path var, i.e. fs.read
) it's crucial to see the whole string.
Right now I get:
(lldb) v8 i 0x00002f51fd0bf901
0x00002f51fd0bf901:<String: "/Users/thlorenz/...">
But I'd like that to be:
(lldb) v8 i 0x00002f51fd0bf901
0x00002f51fd0bf901:<String: "/Users/thlorenz/dev/talks/debugging-profiling-nodejs/demos/demo-3/fs-read.js">
Not sure if this could be a setting that one would change globally that would affect every inspect
or if should be another inspect, i.e. deepinspect
.
It's common practice to install tools using the-g
option, but this seems to fail with llnode:
rchamberlain@ThinkCentre-M57p:~/test$ npm install -g llnode
> [email protected] preinstall /home/rchamberlain/node-v7.7.3-linux-x64/lib/node_modules/.staging/llnode-4a7a78c7
> node scripts/configure.js
Build dir is: /home/rchamberlain/node-v7.7.3-linux-x64/lib/node_modules/.staging/llnode-4a7a78c7
Checking for headers, version is 3.9
Installing llnode for /usr/lib/llvm-3.9/bin/lldb, lldb version 3.9
Linking lldb to include directory /usr/lib/llvm-3.9
npm ERR! Linux 4.4.0-72-generic
npm ERR! argv "/home/rchamberlain/node-v7.7.3-linux-x64/bin/node" "/home/rchamberlain/node-v7.7.3-linux-x64/bin/npm" "explore" "node-gyp" "pwd"
npm ERR! node v7.7.3
npm ERR! npm v4.1.2
npm ERR! It doesn't look like node-gyp is installed.
npm ERR!
npm ERR! If you need help, you may report this error at:
npm ERR! <https://github.com/npm/npm/issues>
npm ERR! Please include the following file with any support request:
npm ERR! /home/rchamberlain/node-v7.7.3-linux-x64/lib/node_modules/npm/npm-debug.log
npm ERR! weird error 1
child_process.js:512
throw err;
^
Error: Command failed: npm -g explore npm npm explore node-gyp pwd
Problem seems to be this line in the config.js script: https://github.com/nodejs/llnode/blob/master/scripts/configure.js#L89
Using npm install llnode
without the -g
option works fine.
Hi, at the moment when doing v8 inspect <address-of-an-array>
llnode would only print the length (and offset & backstore address) of an Array (ArrayBufferView). Are there any support for reading the elements of an Array/ArrayBufferView? Thanks.
On this branch https://github.com/hhellyer/llnode/tree/build_with_latest_lldb I have updated llnode.gyp so that you can build llnode against a locally built version of lldb. (I did this so I can test out my LLDB API changes to add memory region information but it's generally useful and might help other people who hit weird API problems.)
Mac OSX has stricter linking than Linux and requires that you link the dynamic library against any other libraries it will use. Linux only needs the headers.
This difference means the build procedure on Linux only requires -Dlldb_dir to be updated to the location containing your lldb source. (If you have checked out the source it should be <llvm_dir>/tools/lldb.)
The LLVM build process places the built binaries out side the source tree so to build on Mac OSX you need to specify -Dlldb_dir (as on Linux) and a new parameter -Dlldb_build_dir. If you have followed the standard lldb build setup this is likely to be llvm_dir/../build.
-Dlldb_lib can still be used to override the name of the lldb library file but as it liblldb is generally soft-linked to liblldb. it is usually safe to omit it.
(A side effect of these changes is that you don’t need to pass -Dlldb_lib on Linux any more.)
The Mac configure step, when using a local build of lldb is:
./gyp_llnode -Dlldb_dir=some_path/llvm/tools/lldb -Dlldb_build_dir=some_path/build
The Linux config step is:
./gyp_llnode -Dlldb_dir=some_path/llvm/tools/lldb
The normal build instructions are unchanged.
If this seems reasonable I'll create a pull request but it seemed better to discuss it here first.
It's not a problem for us yet, but if there is an issue here then at some point it needs to be fixed.
it would be nice to see the source of JS function frames during bt
.
IE:
> bt
#1 ... foo.js:2:3
> list
console.log(2)
console.log(3)
where foo.js is
console.log(1)
console.log(2)
console.log(3)
I think this should be possible to install on a global install via npm scripts, @indutny is there any reason you didn't go this route?
mdb_v8 supports findjsobject -r
, which will print out all objects that refer to a specific object. This is especially helpful for debugging memory leaks, as you can use it to walk a leaking object all the way back to its root reference.
Does this exist for llnode, and if not will you be open to implementing it?
Hi I have been interested in debugging nodejs in prod for a while but having to install mdb
was quite an issue, it’s really awesome that there is llnode.
I installed llnode on my mac with brew for practice taking core dumps, analyzing points of failure etc it all worked, so I tried setting up with “a real project” using the simple code here so everything is predictable but with a docker container. The idea was to get the core dump move it to my machine and debugging, like I would hope to do in production
This is what I got after moving it to my machine, I’d like to know what I might have done wrong
see also https://github.com/indutny/llnode/issues/24
bt
, nodeinfo
, source
, etc don't work for me.
I did both the simple and hard installs, no difference.
Maybe there's a good reason for this, but notice we're skipping frames 1-4:
(lldb) v8 bt
* thread #1: tid = 13, 0x00007fda23874d44 libpthread.so.0`__GI___pthread_mutex_lock + 4, name = 'node', stop reason = signal SIGSEGV
* frame #0: 0x00007fda23874d44 libpthread.so.0`__GI___pthread_mutex_lock + 4
frame #5: 0x00007fda20ffe212 node`v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) + 290
[...]
They do show up with the normal bt:
(lldb) bt
* thread #1, name = 'node', stop reason = signal SIGSEGV
* frame #0: 0x00007fda23874d44 libpthread.so.0`__GI___pthread_mutex_lock + 4
frame #1: 0x00007fda20b0ed19 node
frame #2: 0x00007fda20ac5e39 node
frame #3: 0x00007fda20a9dd4b node
frame #4: 0x00007fda20a9c9f6 node
frame #5: 0x00007fda20ffe212 node`v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) + 290
[...]
Should "v8 bt" be skipping them?
I have created a docker image that can be used by literally anyone that picks it up for debugging node in prod, I have been able to configure llnode
, lldb
, perf
with help from @hhellyer all that needs to be done is for the user to copy the generated core dump and binary of node that was running on the process into docker see here I'd like to take it a step further and explore the possibility of also generating flame graphs from the core dump
Hello,
First, thank you for the job you've done, llnode is great debugging tool.
mdb_v8 findjsobjects command has one useful feature:
-r Find references to the specified and/or marked object(s)
Is it possible to achieve the same with lldb + llnode? If not, do you have any plans to implement this feature?
Wouldn't it be nice if llnode worked out of the box with node 8? Indeed it would but it won't happen without some even-toed ungulate clipping1. Here is what needs to be done:
tools/gen-postmortem-metadata.py
in upstream V8Bonus points if llnode is taught to print something sensible for WASM and Ignition stack frames/code objects/etc.
1yak shaving
Compile will fail, here is the log output
../src/llv8.cc: In member function ‘std::string llnode::v8::JSFrame::Inspect(bool, llnode::v8::Error&)’:
../src/llv8.cc:245:43: error: expected ‘)’ before ‘PRIx64’
snprintf(tmp, sizeof(tmp), " fn=0x%016" PRIx64, fn.raw());
I tried add #define __STDC_FORMAT_MACROS preprocess, then compile passed.
But, when I open lldb, help v8
command get nothing subcommands.
(lldb) v8
The following subcommands are supported:
The following subcommands are supported:
My g++ version
$ g++ --version
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
lldb version
$ lldb --version
lldb version 3.4.2 ( revision )
Could we have a v8 findleaks command, that found leaked objects? This might be something that requires v8 changes to understand allocations. Plenty of prior work in this area, of course, even one from my CEO: http://web.stanford.edu/class/cs343/resources/cs343-annot-purify.pdf
Currently we do this for v8 findjsobjects to work:
# ./scripts/readelf2segments.py /var/cores/core.node.30833.1468367170 > core.30833.ranges
# export LLNODE_RANGESFILE=core.30833.ranges
It might be nice if llnode could do this automatically, even if it means llnode shelling out to call readelf and scraping the output (if that's possible in lldb).
Using lldb 3.8 on Ubuntu 16.04 (4.4.0-34 kernel) with latest llnode. Running on node v6.6.0 Release build.
Getting error: Failed to evaluate expression
when attempting to inspect
a function address. Here's an abbreviated log:
node_v6 ((v6.6.0)) $ lldb-3.8 -- ./node -e 'Buffer(1024).indexOf(Buffer(3))'
(lldb) target create "./node"
Current executable set to './node' (x86_64).
(lldb) settings set -- target.run-args "-e" "Buffer(1024).indexOf(Buffer(3))"
(lldb) b 'node::Buffer::IndexOfBuffer(v8::FunctionCallbackInfo<v8::Value> const&)'
Breakpoint 1: where = node`node::Buffer::IndexOfBuffer(v8::FunctionCallbackInfo<v8::Value> const&), address = 0x0000000000f53b40
(lldb) v8 bt 7
* thread #1: tid = 2513, 0x0000000000f53b40 node`node::Buffer::IndexOfBuffer(v8::FunctionCallbackInfo<v8::Value> const&), name = 'node', stop reason = breakpoint 1.1
frame #0: 0x0000000000f53b40 node`node::Buffer::IndexOfBuffer(v8::FunctionCallbackInfo<v8::Value> const&)
frame #1: 0x000000000090107c node`v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) + 348
frame #2: 0x000000000093530c node`v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::(anonymous namespace)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>) + 1052
frame #3: 0x000000000093f4e8 node`v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) + 456
frame #4: 0x00000ccd50e092a7 <exit>
frame #5: 0x00000ccd50f3fa52 bidirectionalIndexOf(this=0x00001b416de04381:<undefined>, 0x00003628897e5029:<ArrayBufferView 0x0000000001baad40+0:1024>, 0x00003628897e52c1:<ArrayBufferView 0x0000000001baad40+1024:3>, <Smi: 0>, 0x00001b416de04381:<undefined>, 0x00001b416de043c1:<true>) at buffer.js:559:30 fn=0x000036288973e9a9
* frame #6: 0x00000ccd50f3f401 indexOf(this=0x00003628897e5029:<ArrayBufferView 0x0000000001baad40+0:1024>, 0x00003628897e52c1:<ArrayBufferView 0x0000000001baad40+1024:3>, 0x00001b416de04381:<undefined>, 0x00001b416de04381:<undefined>) at buffer.js:622:44 fn=0x00002272434079c1
(lldb) v8 print 0x00002272434079c1
0x00002272434079c1:<function: indexOf at buffer.js:622:44>
(lldb) v8 inspect 0x00003628897e5029
0x00003628897e5029:<ArrayBufferView 0x0000000001baad40+0:1024>
(lldb) v8 inspect 0x00002272434079c1
error: Failed to evaluate expression
At the end I also show that print
works on a fn address, and inspect
works on an object.
Currently findrefs has to rescan the entire heap each time. Could an index or some cache be added to improve performance of this when using it multiple times?
Should dump all Code
instances and their function names. Should be useful for on-demand profiling.
See https://reviews.llvm.org/D19516. The lldb system plugins directory for linux is now /usr/lib/lldb/plugins
. I think this happened in lldb v3.9. So it no longer finds the llnode plugin if it is installed in /usr/lib/lldb
(which is where the current llnode makefile install target puts it).
They say that earlier lldb versions do a recursive scan from /usr/lib/lldb, so lldb v3.8 should still find our plugin if we install it in /usr/lib/lldb/plugins
The current implementation of the findjsobjects command doesn't include JavaScript strings as part of the totals. This is a bit of a problem if you are trying to use the output to diagnose what is filling the heap as strings are usually the largest consumer of heap space.
Example:
(lldb) v8 findjsobjects
Instances Total Size Name
---------- ---------- ----
1 24 (anonymous)
1 24 JSON
1 24 process
1 40 Arguments
1 56 DefineError.aV
1 56 MathConstructor
1 56 RangeError
1 96 MyObject
1 104 TypeError
1 136 Module
6 336 Error
10 800 (ArrayBufferView)
13 1768 PropertyDescriptor
16 392 (Object)
17 544 ContextifyScript
19 1064 NativeModule
101 3232 (Array)
314 17056 Object
6013 47920 (String)
I've pushed a branch with a patch for this (I'll add the PR in a moment). There might be a reason for not including strings in that output but I think not including them is more likely to be misleading for users trying to understand what is filling the heap.
@indutny @hhellyer
Hi Fedor, we've been discussing the possibility of an NPM/Javascript API on top of llnode, so that diagnostic tools could be written that exploit the llnode+lldb libraries. An alternative to using the line-mode lldb debugger + plugin itself to read a core dump. For example, an interactive web viewer for core dumps that are stored on a node server. Also possibility of user add-ons written in Javascript.
Here's a very basic prototype, just to load a core dump and list the thread stacks:
indutny/llnode@master...rnchamberlain:llnode_api
Any thoughts? Would need to design the API, make the calls async etc etc
Hello,
I need your help for building lldb-mi static library in mac os.
Many thanks
If you run v8 inspect -k 0x0 on Linux then llnode seg faults.
This is a problem with the handling of the command arguments in CommandBase::ParseInspectOptions.
Would it make sense to have a "v8 workqueue" or "libuv workqueue" command, that showed what was waiting and their callbacks?
Maybe something that made use of process._getActiveHandles(), process._getActiveRequests(), or process::getActiveHandles.
$ ~/Code/indutny/llnode/scripts/otool2segments.py ./core
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/objdump: './core': Invalid argument.
fatal error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool: internal objdump command failed
cc @hhellyer
This isn't so much an issue, as just a request for advice.
I'm interested in hacking on llnode, but I'm brand new to lldb. Do you have any advice on how to debug the llnode extension while it's running inside lldb on a node corefile. I did a search for debugging lldb under lldb, but didn't turn up anything obvious.
Thanks.
Is it possible to detect that there is a version mismatch between node and the core file, and print an error or warning? Here I'm using the wrong version of node with a core file, and it doesn't complain, but it would be helpful if it did.
(lldb) target create "node" --core "core.3397"
Core file '/mnt/core.3397' (x86_64) was loaded.
(lldb) v8 nodeinfo
No process objects found.
Great work Fedor. Any idea whether it's possible within LLDB to find heap objects? Something along the lines of https://github.com/joyent/mdb_v8/blob/master/docs/usage.md#finding-javascript-objects would be extremely helpful.
Actually, now that I think about it, what about porting some of the functionality over from mdb_v8 to lldb? The code for mdb_v8 is already open source, I wonder if it would be easier to try and port that library? In any case we should talk about your work with llnode here at an upcoming nodejs post mortem working group meeting.
It would be lovely if we could inspect C++ from object's internal fields. This will require some additional constants to be exported.
We shouldn't need to pass an InspectOptions structure in to GetTypeName. Removing it would allow us to tidy up the places that call it, mainly llscan.cc.
#92 tracks documenting the problem, but... maybe there's a workaround for this that we can use for lldb 3.8? Just wanted to post this as an issue in case someone figures it out. I don't know how far away it will be for lldb 4.0 to be widely available.
I think we should have one.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.