1. 15

So I recently saw this posted on HN, which shows the call stack of a Java application from the point an http request hits Tomcat down to the point something happens in a database via JDBC.

I get that it’s just a java call stack, but this diagram made me very happy, as for some time I’ve had in mind the idea to try to visualise every abstraction from a transistor up to a user for a reasonably general use case such as looking at a web application in a browser.

My question to lobste.rs is - do any of you have, or could you suggest a reasonably direct way, to get hold of or create similar ‘call stacks’ for other parts of the whole stack of abstractions?

No, I’m not even sure exactly what it would look like, and yes, at some points it would need to ‘branch’ and allow visualisation of different stacks of abstractions that are working simultaneously, but the goal here is a rough estimate of (perhaps for one particular user action), a single-ish critical path of information flow through layers of abstractions.

This is just for my personal interest, although I do believe that one of the wonders of the world is that the hundreds of layers of abstraction between transistor and user seem to work mostly ok most of the time - and a visualisation of the entire stack would be useful in arguments about global technological complexity and sustainability, etc.

Edit: For clarification, not looking to build the stack myself or understand a simplified stack, I’m suggesting it would be useful to visualise all the layers of abstraction in a real environment.

  1.  

  2. 14

    I’d strongly, strongly suggest checking out the book Code, which our very own @tedu covered in his book chapter blogging.

    The other one is NAND to Tetris, which I have no direct experience with.

    1. 2

      Thanks! These are nice. I love this from the intro in Code:

      Metaphors and similes are wonderful literary devices but they do nothing but obscure the beauty of technology.

      NAND to Tetris helps with an example somewhat dedicated device where the abstractions can be minimised; I was also hoping to illustrate abstractions in a real world computer, including modern day OS, network layers, CPU, etc.

      1. 1

        I can second that recommendation for Code. It was one of the earliest things I read when I was learning how to program computers, and gave me a half-class head start on a Digital Systems class in college.

        It doesn’t have some more modern stuff like multiple layers of caching that we have to thank for branch prediction and Intel CPU bugs or the TLB, but it’s excellent for getting a good feel for how things compose up to the point that reading about those things makes more sense.

        http://www.megaprocessor.com/ is also an interesting project in this space

        If you want to illustrate the whole stack, from transistors up to the UI, you’re probably looking into writing or modifying a Virtual Machine, or daisy chaining flame graphs, packet captures, UI-framework debug drawing, and a dozen other things that are used for debugging different layers of the stack.

        If you just want to show the whole stack for a particular operation, just be cognizant that at the transistor level, you’re dealing with millions upon millions of gates in most calculations.

        1. 1

          If you just want to show the whole stack for a particular operation, just be cognizant that at the transistor level, you’re dealing with millions upon millions of gates in most calculations.

          Hopefully that visualisation would be abstracted away, e.g for simplistic overview of modern memory hardware, I’m just imagining something like:

          Control Logic
          Scheduler
          Address/Data Line Decoding
          Flip Flop
          Logic Gate
          TTL Logic
          Transistor
          
      2. 8

        Check out Oberon http://www.projectoberon.com/.

        In 300 pages, and starting with an FPGA (you can get one for relatively cheap), you build a CPU on for the FPGA, a compiler targeting said CPU, an OS using said language, a simple UI and simple network stack for said OS.

        1. 4

          I’m not sure how one would. I’m running a Lua program, and right now, the call stack of the Lua code looks like:

          (main_thread)
              POLL:events()
          

          It’s not very deep (there’s no name to the main function—it’s just code, and right now, it’s waiting for events). But Lua is written in C, and when we switch to that view:

          main()
            lua_pcall()
              luaD_pcall()
                luaD_rawunprotected()
                  f_call()
                    luaD_call()
                      luaV_execute()
                        luaD_precall()
                          polllua_events() -- POLL:events()
                            __epoll_wait_nocancel() -- system call
          

          Here, polllua_events() is the Lua function POLL:events(). The “call stack” of the CPU is a misnomer—there really isn’t a “call stack” per se. I mean, we got to this instruction:

          call   404a00 <epoll_wait@plt>
          

          And at a high level, the CPU fetched the call instruction, decoded it, grabed the operand (the address), pushed the next instruction address on the stack, set the PC to 0x404A00 and into kernel land we go. But that’s what the CPU does, it fetches and executes instructions. Much more below this, and there’s not much to actually see. You could do something like the Visual 6502 but a modern chip is way more complex (with out of order execution, register renaming, caching, etc).

          1. 1

            I realised while actually trying to map out an example that software and hardware need to be modelled separately, but can be anchored together at certain points.

            Software goes down to machine code instructions, and potentially microcode then after that you’re on bare metal which can be further abstracted down several layers and branches.

            Also was interested in the reality of a general user facing desktop program such as a browser, where as well as down you go up through several branches of abstractions (e.g. rendering to screen) before you get to the user.

          2. 4

            “Transistor” is an abstraction: it’s various semiconducting materials combined to yield particular outcomes at one terminal when a voltage is applied between two other terminals. “Voltage” is an abstraction: it’s the potential for charge to flow between two define surfaces. “Surface” is an ab

            1. 2

              I was waiting for this, you win the prize, but only second place, as you didn’t abstract up from ‘user’ too.

            2. 3

              I don’t know the answer to your question, but with Linux perf tools you can go all the way from a node express handler thorough the kernel stack down to pretty low level network stuff like an interrupt generated from a packet hitting a NIC.

              1. 2

                This is a fun question.

                I feel like what you describe as “a single-ish critical path of information flow” sounds interesting, but it isn’t really how I usually think of a call stack. If we think of a call stack visualization as showing us (the control-flow part of) the state of a program at a particular time, we could just extend that metaphor to other layers of the system. This would get so big, it’d be great to see it as an interactive zoomable thing.

                Unfortunately I don’t know of off the shelf tools for exactly this. I think @yumaikas’ suggestion to look at virtual machines is a good start, since I assume there’s a way to inspect their state (haven’t done that myself) - you could also look at tools like valgrind for collecting lots of process-and-thread-level detail, and once you want to collect info on the hardware, run things in a full-system architectural simulator like Gem5. It’s real slow but you can look at everything in a very realistic system that runs real software.

                I’m imagining a layered visualization with the following rough layers. Each level down would try to connect to the level above if possible without being too confusing, with lines showing e.g. how a virtual address in a text segment in the heap gets mapped down through memory and caches into the program counter in the processor.

                • Your process’s address space layout on the top, and the call stack(s) for each thread as well as the heap shown. We’d highlight the currently executing stack frame, and the location of the corresponding code in the code segment. - Visualize files that the process has open and show them in the address space if they’re mmapped
                • Visualize linked libraries - show which libraries are loaded (and where in the address space)

                Then at the next level you see some kernel structures, and I think you could get at some of this with perf tools.

                • Visualize the process’s physical memory use as highlighted regions in the context of what else is in there. Show what parts of the program’s address space are resident in physical memory? Show file contents in the buffer cache. Show other buffers the kernel keeps for e.g. network devices, that the process might have opened.
                • You could show the current thread in the kernel’s scheduling structures.

                Now the hardware, which you’d need to instrument an architectural simulator to get:

                • Show contents of caches, especially interesting in a multicore system with coherent caches, highlight that some cache lines are in multiple places at once.
                • show cpu pipeline with our current instruction from way up in the text segment, making its way thru the pipeline. (see the gem5 o3 pipeline viewer for something interesting if not totally useful here.)

                As for anything lower level than this, I think you’d have to do some editorializing because of the scale of things. You could say that you’re on a RISC-V core, so you can actually get access to the hardware description, and just show e.g. one adder in one stage of the pipeline that’s executing our instruction, and just fake its contents for the visualization. I’m not really familiar with tools at this level, maybe there’s a better way, I’d be curious to hear.

                1. 2

                  After looking at the front page of the gem5 wiki you linked to, I decided to do a web search for gem5+screenshots and I got this result:

                  …I think that GUI is an addon call streamline? I just scrolled though looking at the pictures. :)

                  1. 1

                    This would get so big, it’d be great to see it as an interactive zoomable thing

                    Yeah I imagine it to be huge. My ballpark guess is that for a piece of desktop software modelling from ‘User’ or ‘Application’ abstraction all the way down through assembly code on the CPU to e.g. firmware running in a NIC or keyboard controller there are 500 layers of software abstraction if you could somehow find an example of a ‘longest path’. (This is using the definition of each layer of abstraction roughly corresponding to e.g. a ‘class’ in OO code or function / module in non-OO, the point being abstractions rather than LoC)

                    I was hoping to get pieces of the whole thing from people who might specialise in those parts and manually stitch them together.

                    It sounds like you’re going into visualising the actual content of each layer of abstraction? That sounds like a huge challenge, but would be fascinating; I was just going for getting a single long list of labels for each layer (i.e. class/function name) ;-)

                  2. 1

                    Couple of notes as I’ve been thinking about this today a little bit and putting some of the layers in a spreadsheet:

                    • Hardware and software are obviously two separate sets of abstractions, that will have certain points that can be anchored to each other
                    • To facilitate thinking about it like a tree, the points at which hardware devices connect to each other at the same level of abstraction is a ‘bus’, and in software would correspond to one layer in a call stack (better visualised as a flame graph)
                    • Here’s a good example of an illustration of levels of abstraction in the Linux Kernel. I might try to imagine a particular couple of events and map a single entire call stack through that.