53 comments

[ 2.7 ms ] story [ 105 ms ] thread
The visualization aspect looks quite interesting, but I'm not sure how many things this can do that a quality IDE already doesn't.

The example uses the author mentions ("Following code paths from method to method", "Finding where an interface is implemented and which methods get overridden", "Exploring dependencies between types and functions") all sound like pretty standard features today. Plus with an IDE you get the benefit of having them right there in the editor/debugger/etc. and much more.

I would however really like something for inspecting the run-time structure of an application's objects. Most debugger views are really clunky for looking at large amounts of data, and even the pretty-print features often don't help much. Having a zoomable graph with the objects right there in front of me would really bring my productivity to the next level.

The DDD debugger has(had ? Not sure it's maintained anymore) pretty useful visualization of data - e.g. like https://bcaptain.files.wordpress.com/2013/06/ddd.png
That's a good start, I'll try that out when I have some time.

This specific screenshot however - a linked list - shows a situation where you most likely don't care about the exact structure like this - you only care that it's a list of elements, so most debuggers would show it to you like that - just as a list. The specific raw structure only obscures the parts you care about.

Visual Studio allows describing your custom data structures like this using XML files (https://msdn.microsoft.com/en-us/library/jj620914.aspx), or even custom graphical controls (https://code.msdn.microsoft.com/windowsdesktop/Writing-graph...) and gdb allows you to write a pretty-printer in Python (using an API which I can't find any documentation for :/ ), but it's all kinda clunky and still doesn't deal well with large amounts of data.

My ideal tool would be something that combines both of these things - you have

- An object graph that you can zoom in/out of that shows raw objects (just like in that DDD screenshot).

- An easy way to describe a custom view for your own objects (you can also switch to the raw view for an object, or switch between different defined views, etc.). Just like VS/gdb/lldb allow you to do, but a lot easier and more powerful. You could for example view a specific dictionary that contains complex objects in a tabbed interface, where the keys are tab titles, etc.

- A way to live edit these custom views - so that you can rapidly create them without restarting the debugger and restoring the state many times (Visual Studio supports this for the .natvis files).

- A powerful searching/filtering/transformation mechanism (e.g. for every object that satisfies this condition, show only this property and sort by that, etc.).

- Some way to save these configured views + filters + transformations, etc.

gdb's pretty printing API is documented in the texinfo manual, most usefully this page: http://sourceware.org/gdb/current/onlinedocs/gdb/Writing-a-P.... I use it a lot, and love it, but I haven't used VS's XML thing to compare. For looking up API documentation I usually drop into `python-interactive` from within gdb and do e.g. `help(gdb.printing)`.

Though, you're still limited to string representations that way. If I want to examine a really complex datatype, I'll sometimes write a gdb command to dump some kind of GraphViz output and run it through dot. Or come up with a json representation and explore it using jq or ad-hoc python filters.

Looking at your ideal tool checklist - I think it would be possible to build this on top of gdb. I wrote a prototype a while ago that would launch a HTTP server from the gdb prompt, and then you could control it via websockets. Never took it past recreating the prompt with an <input> tag, but someone who was better at front-end work could conceivably do all sorts of nifty data visualizations with it.

You would care about the pointers in that linked list if one of them had gone awry.
I'm working on a new kind of tool on those lines—would appreciate any feedback on whether the format I've designed so far would be useful to you (or others). http://westoncb.com/projects/avd

(I've been thinking about ways of getting to deal with arbitrary object graphs, but an important requirement was to keep it language-independent, so it just deals with common data structure formats at the moment: lists, trees, tables, graphs, hashmaps, etc. —my thinking is most difficult to debug algos are performing operations on these anyhow.)

This is amazing! Is the UI OpenGL? Is the visualization program a separate process? I'm guessing the main process sends info about the monitored data structures over a socket?
> I'm guessing the main process sends info about the monitored data structures over a socket?

Correct!

The UI/server is Java/OpenGL.

The symbolic lookups and diagrams described are already implemented in modern IDEs. People who rely on relatively simple text editors and text / regex search may be less aware of this, of course.

I do agree that visualizations are lacking. In part this is because of difficulties in complete analysis, and rather than do half a job, they don't do any job at all. And in other parts its because different tools do a better job: for example, instrumenting profilers are better at showing control flow.

I generally want three kinds of things in my head when reading code: control flow, data flow and data shape.

But there are different resolutions to this. Control flow may be simple, at the method level; or it may be more complex, with asynchronous callbacks, queuing systems, RPC, web service requests and orchestrations controlled by configuration.

Data flow may be simply knowing where in the code a particular attribute is updated and where it is read and used to make a decision. But it's also about where the data came from ultimately - what all the ingredients are that go into its formation - and also what other data it in turn affects.

Data shape has to do with the simple shapes of structures, but diagrams are scarcely needed for that - a glance at the definition of the structure is enough to commit it to short-term memory. More interesting is global invariants, local invariants, longer chains (how you get to one distant structure from another via links), the database model, configuration data model, static data vs dynamic data.

Most of the interesting information is at a higher level than can reasonably be analyzed in most Turing-complete languages. The best info comes from profiling or debugging.

Can visual studio do the graphical representation of a C# codebase? A javascript file?

Looking at Coati.io it can't do those languages yet, but the graphical flow chart looks really amazing to me and I'de love to try it out on my own code.

Class descriptions are not only put into diagrams, they're interactive controls. Ten years ago, the model-driven development trends focused on making more and more of the code driveable by UI representations and code. People tried to model code using UML or UML-like representations, with two-way links. Tooling inferred models from existing code.

For more current approaches, see "Code Maps" in the VS 2015 release notes here - https://www.visualstudio.com/en-us/news/releasenotes/vs2015-...

Doing proper control flow is done less often for the reasons I explained. Profilers do it better. See e.g. the Call Graph in AQTime: https://support.smartbear.com/viewarticle/43205/ - it lets you drill through the callers and callees of each function as seen in practice. This means it can see through polymorphism, reflection, dynamic loading, function pointers - all the things that make static analysis infeasible.

(Note: VS Entreprise is $6000 away. I see a market for cheaper code visualisation tools...)
Visual Studio does, but only in the "Enterprise" configuration($6K per seat). I haven't used it to be able to speak to its quality.
Well you have the class view which tells you a lot. And you have the object browser which is very useful too. It's not quite the same but I think it tells you a lot about a class without having to do masses of reading.
In the past working on a large c++ codebase I've tried using a debugger to get an understanding on how a function gets called.

Even the debugger fails when you come across indirect calls. Also in gdb multiple threads make it easy to miss something when stepping through code.

Debuggers aren't always the best tool for figuring out control flow - accidentally step over something and you can miss it - but they are useful to explore the map of data in memory. Some debuggers let you expand out the map hierarchically, others use separate windows (e.g. the old Borland IDE Debug Inspector windows).

For exploring control flow, an instrumenting profiler is really useful, if you can easily isolate a representative code run.

A few years ago now at my work I wanted to get my head around how our large c++ code base worked I used a tool called Doxygen it was pretty useful for reading and visualizing the source code. It output html pages and could generate dependency graphs etc automatically would show you which file a function came from let you browse by class or by individual source files etc. You could easily see every file a class was referenced in etc.

Downside was it took a long time (many hours) to scan the entire codebase.

I would strongly suggest you check out rr - Mozilla's Record and Replay framework https://github.com/mozilla/rr . Don't let the name confuse you - it's not specific at all to Firefox. It can record a process's execution and replay it in gdb, and lets you step and continue backwards. This last point is super powerful - let's say you see that some variable has an unexpected value. You can set a watchpoint on it, then reverse-continue and gdb will stop at the point it was changed. It has been invaluable in finding some tricky bugs. Apart from that, you can also use it to explore the execution of a program, since you can freely step backwards and forwards in time. You also get a deterministic thread execution sequence each time. That might be either good or bad, depending on what you want.
I see comments that "an IDE can do this". Perhaps so. But there are lots of data-discovery missions where you need to learn a codebase quickly that you won't be fully compiling or building yourself. Perhaps you need to replicate an approach, or understand where something in your code needs to change in order to work with the other code that you can't touch. Tools like the OP's could become very handy in understanding a codebase without bringing the entire thing into your IDE.

I agree that some pieces only are really comprehensible runtime, but I applaud tools that reflect the need to learn a codebase without necessarily having to (or being able to) bring all the code into your IDE.

> learn a codebase quickly that you won't be fully compiling or building yourself.

I used early versions of Coati (0.5 I think) and it used clang for the backend, loading a new project took ages, probably longer than compiling it in my case. I should try again as they released 1.0 not too long ago.

As someone looking in from outside, the Chrome codebase is a treat. Anecdote.

Once during the start of my career I was asked to implement a caching layer in an HTTP library. I had no idea what it was, so there was some reading up to do. There were the excellent guides from mnot.net, as well as the nicely written RFC 2616. But Chrome's code was the best of all - https://github.com/adobe/chromium/blob/master/net/http/http_.... If you want to know for a fact how Chrome decides caching, that code is kind of the heart of it.

Recently I needed to retrieve the "Rendered Font Name" that is available in the DevTools "Computed CSS" section. This is the name of the system font that Chrome finally picks, based on the Font-Family property. This is platform specific and so not in DOM, nor available to Chrome Extensions directly. The only way it could be done was by making an extension running in debug mode and communicates to the browser thru its remote debugger protocol. (This part of the documentation is lacking, but it is an esoteric topic anyway). The good news was that the code that does this is well encapsulated and could be easily extracted into a command line utility. For the curious: https://chromium.googlesource.com/chromium/src/+/master/thir... (there is a nugget about real-world software in the comments)

This is, at a surface level, good code to me. Things are easy to find, there is a rhyme and rhythm to the system, and feels welcoming. The thoughts of the people who designed that system over years would be great to hear. Most writing about good code on the internet comes from an OO background, mostly wrt information systems. I wonder what people who've written these systems have to say about building and engineering complex software.

I'm not a fan of the comments. There's comments that are utterly useless like:

    // Of course, there are other factors that can force a response to always be validated or re-fetched.
Gee thanks, what might those factors be? A comment like that is worse than not commenting at all.

Or the real ones that get my goat, comments which just tell you what the code that follows obviously does:

  // If there is no Date header, then assume that the server response was
  // generated at the time when we received the response.
  Time date_value;
  if (!GetDateValue(&date_value))
    date_value = response_time;
There's even a line (1019) where they divide by 10 but don't explain why they do it. That's what I personally would comment.

The code itself is fairly easy to read (though I do wonder why they bothered using TimeDelta as it just seems to make the code more complicated, and results in confusing code like this:).

    return TimeDelta();  // not fresh
I think the comment you mention makes perfect sense within the rest of its context. It's saying the rule for determining freshness is "response_is_fresh = (freshness_lifetime > current_age)", however that's not always the case. And looking at the code, you can see it returns true early if lifetime is equal to a new TimeDelta.

The GetFreshnessLifetime function below it then covers the additional cases where it returns that. Such as the headers being set to not cache, or the expiry time being earlier than the response's time (or current time if none is provided).

I think it also makes sense to assume the comment is letting us know that just because HttpResponseHeaders::RequiresValidation returns false, that doesn't mean that's the only thing that can make it require/not require a re-fetch.

> There's even a line (1019) where they divide by 10 but don't explain why they do it. That's what I personally would comment.

This is covered near the head of the function, line 951. Using a constant such as heuristic_scalar instead of simply using '10' would be more readable though.

While I'm not on the net team - I just mangle the UI and annoy Ben ;) - I can wager a good guess: The net team used TimeDelta because for any software project of Chromium's size strong types are crucial.

If they just returned an int, you'll sooner or later see it passed through five different places, and at the end location, nobody remembers if it's ms, time ticks, seconds, or even a unix time instead of a delta. TimeDelta removes that question.

It also does things that have subtle issues you might miss if you did it "by hand", like saturated adds, multiplying with an integer value while handling overflow correctly, etc.

These things might be overkill for smaller project, but once you have something with hundreds of contributors, every little bit helps keep the code base sane.

As for the division by 10 - look up at line 953 :)

Yes, it should be a named constant. And some of the comments could certainly be better. It's a work in progress. (And if you want to help with that work, we happily accept patches!)

Yes, that seems like a good reason to use TimeDelta.
Chrome team co-founder/engineer/etc. here. Glad you found it useful! Some components like our net stack are particularly cleanly factored. Others have more room for improvement.

I would say that at the beginning of the project (2006-2008) we didn't have so much of a focus on platform design, just on shipping a browser as quickly as possible. Some of the abstractions from that era haven't stood the test of time as the project has scaled to many platforms, features etc.

Over the course of time we've had various refactoring projects to try and pay down some of the technical debt. The first major one was the "content refactor" from 2011. This led to the separation of the multi-process browser shell from the UI layer, which has allowed for other chromium-derived browser apps to emerge.

Today, we've observed that even this layer is a bit too complicated, so we're running more projects to try and modularize it a bit more. My mental model is that the browser is kind of like a set of system services for an ephemeral app runtime, and it's good to imagine what the APIs & separation between those things should be. To aid this we've developed a new suite of IPC tools which are way more useful than the original stuff we have used for much of the lifetime of Chrome.

Anyway this kind of thing requires an ongoing investment and a set of people who thrive on the art of API design and in grungy, challenging refactoring work. I probably have many more thoughts on this topic but this'll do for right now :-)

Absolutely stoked to read your response. Thank you sir.

There is a dearth of quality conversations on the internet about good code in a real-world messy context, mostly because the people who're doing serious work don't have the time to talk about it. Would be a good thing if you write more. In fact you folks should be writing books!

One maybe interesting anecdote.

May 2015, a few Chrome old timers reacting to the complexity of the code, decided we should try and build something new. This is nothing new for me personally (having worked on Netscape, Firefox and Chrome, and various false-starts along the way). We decided to design a browser based on a service-oriented architecture, using our new IPC and bindings tool (Mojo). This project was called Mandoline. We got a shell up and running that could complete some of Chrome's telemetry test suite. Performance was good. The architecture was clean. Problem was, the browser didn't do all that much. While a team of 6-7 people might have been able to build a browser 10-15 years ago, today browsers are just too complex (in feature requirements).

So our options were to try and convince the Chrome team (huge org) that they should drop everything and help us build this prototype into something real (unlikely, many past examples of failure of this kind of thing - see: Gecko transition/Netscape 6), or to find a way to bring this architecture into Chrome. The first not being a real option, we settled on the latter.

OK so you might ask - why labor under this delusion of building a new browser from scratch at all? Why not just stay within the confines of Chrome from the start, and look at the incremental projects that can be done to pragmatically improve things? My answer is that incrementalism should not be a destination or a goal. It's a tool that helps you get somewhere interesting. If you don't know where you're going, you're lost and incrementalism is just a delusion to trick you into thinking you're making meaningful progress. In the suffocating confines of a massive codebase, it can sometimes be hard to see the forest for the trees. It can be very valuable to step aside and try something else. Stepping aside can be creating a branch and hacking away liberally, or creating something entirely new. The other benefit of doing this is that it doesn't distract or further complicate the shipping product. But then bring the learnings back into the main line. And hopefully in your project you have leads on the main line willing to learn from such discoveries. On the Chrome team we're fortunate that we do.

So this is what we're doing now. We're bringing a service-oriented architecture to Chrome. A few of us have a pretty good mental picture in our heads of what the end state looks like (roughly) and we're using incrementalism to nudge the Chromium codebase there over a few years. The value in this approach is we get to validate our ideas against all the different platforms & features Chrome supports and test it on all of Chrome's users. It means if our changes land & stick that they really are by definition "good". By the end (if there ever really is one) we will have rebuilt much of the system architecture, while shipping every 6 weeks.

I was interested recently to read a similar story here about the plans in Firefox to integrate some of Servo into Gecko. The rationale was very similar. The reality is that you can't burn your user base by neglecting them while you build the massive new thing, or by expecting them to switch to something else. Instead you have to embrace the complexity & figure out how to work within it, while not giving up your dreams.

wow, that was amazing. Really nice to hear interesting stories like this.

Your take on incrementalism is interesting. I remember Joel Spolsky used to say never rewrite software from scratch as you will introduce new bugs. But I guess a well-balanced approach is always beneficial.

And yea, modularising Firefox and slowly replacing part by part with Servo is indeed a great idea.

I'd like to add my grain of salt here: I can agree about the don't rewrite from scratch "rule", but only depending on the context -- and also only when looking at all the details of a given project.

When bengoodger says "reacting to the complexity of the code", I doubt this is as messy as what can be found in some private codebase produced over a decade by some less-than-google-level employees who since left. So while the never-recode myth has some value in somehow (at least) good code bases, and depending on the angle of view (to even judge whether an approach constitute a rewrite or not), it also has a rather big cost, when some manager try to use it on projects far beyond the non-return point to justify forcing engineer to swim in the septic tank instead of doing any valuable work.

After all, for each Netscape (which BTW, involves a quite good amount of technical and commercial mix, and even then Mozilla is still with us today...) I could ask why a corresponding NT has not evolved from consumer Windows... (some parts were shared, but again: the angle of view...)

Not a counter-point at all, just an aside: consumer windows and NT were fundamentally different software. The "Showstopper! the Breakneck Race to Create Windows NT and the Next Generation at Microsoft" book is a packed guide of what happened at Microsoft at the time and covers the differences. Very interesting read.
I agree, that book is excellent.
The chrome code base is not easy to understand and it keeps getting changed even when it really doesn't need to. For example it used to render most of the colors here Gradient.cpp. Now its rendered in some other place and its hard to figure out exactly where.
Chromium Code Search sounds sort of similar to Hoogle. Can anyone with experience of both confirm this?

Also -- sort of offtopic, but motivated by TFA -- I'd love some way to find out about quirks that native speakers of $lang have when they speak $otherlang.

A few common "tells" that I know, for $otherlang = Englishf are Hyphenating-Things-Like-This and also spaces before exclamation points or question marks !

"spaces before exclamation points or question marks !"

This is probably related to typographic rules. For example in french you put a space before and after double punctuation marks (!?:;" etc) and a space after single punctuation marks (.,) one exception is the single quote which should not be precedeed nor followed by a space.

This reminds me a little bit of the IDA disassembler. There are moments where you might face production issues with external JS code and have no source maps available. A tree view to dissect what is going on would be useful in these situations.
This tool looks quite useful for weird build environments with tons of completely undocumented native code such as AOSP (Android) where IDEs regularly fall over.
Trial does not allow to try it... You can only try it on some predefined small projects. As I understand the only option for me to try it is to buy a license. Ok, author says that I can get refund in 1 month if I will not like it. But anyway - too much movements for trying. Author please consider to actually give an option to really try it. Another: please add retina icons.
I was curious about this as well. You can mail them for a "real" trial.
Trying should be made more accessible.

"Looks" promising.

This is really cool. One killer feature that I don't know if you have considered is some kind of visualization of the stack trace, showing how control passes between the objects & functions. That would really help with dynamic languages, or large codebases that have been Greenspun-d heavily. It would be very difficult to implement, especially in a cross-platform way, but I bet if you pick a language community like c++, java or something else and focus on it, you might have better results.
Reminds me of the code bubbles and debug-canvas I used to use in Visual Studio
(comment deleted)
Java provides an easy way for tracing control flow during program execution: when I join a new contract and am responsible for maintaining legacy java code, I use AspectJ with logging aspects with before and after pointcuts. This helps me figure out the application control flow.

I wonder if something similar is available for C/C++.

I feel obliged to mention this talk by Zed Shaw: https://vimeo.com/53062800

It gives an overview and interpretation of a body of neuroscience research in the context of teaching programming. I can't quite summarize the whole talk succinctly (and don't want to lure anyone with catchy titles either) — but my takeaway from it is that those "visual" programming tools are mostly useless and not going to help significantly.

The reason for that being how the brain works: switching back and forth between "visual" and "linguistic" cognition is hard, and requires specific training to do efficiently. Please turn to the talk for references.

Incidentally, Webkit is the hairiest pile of code I've ever seen.
If it was hosted, and you could point it at a GitHub repo, I'd use it.

Otherwise I'd rather use whatever IDE JetBrains has for it. It might not have the exact same capabilities (or maybe it does, I didn't look closely enough) but why use another tool and context if the current one is good enough.

This company is tackling the same problem with a slightly different approach. I like that they have made their tools open source and modular. Theoretically you can use their system with any language by writing a few modules.

https://sourcegraph.com/