# AST Profiler The biggest project of the five. It reads the srcML XML representation of a C++ file, builds an abstract syntax tree, and rewrites that tree to inject profiling counters. The output is an instrumented copy of the source that counts how many times each function and statement runs. srcML is a tool that wraps source code in XML tags marking its syntactic structure (``, ``, ``, and so on) while keeping the original text intact. That format is what lets a program treat code as a tree and then print it back out as code. ## The two classes - **`AST`** is a node in the tree. A node is one of three kinds: a `category` node (an internal node with a tag like `function` and a list of children), a `token` node (a piece of source text), or a `whitespace` node. Category nodes hold children; token and whitespace nodes hold text. The class has the full set of value semantics: copy constructor that deep-copies the child list, destructor that deletes children, swap, and copy-and-swap assignment. - **`srcML`** wraps a whole file: a header string and a pointer to the root `AST`. It forwards the transformation calls down to the tree. The instructor provided the `AST`/`srcML` skeleton, the XML reader, and the tokenizer. I implemented the transformation methods that walk and rewrite the tree. ## The pipeline ```mermaid flowchart LR A[foo.cpp.xml
srcML input] --> B[operator>>
read header + tree] B --> C[AST::read
recursive parse into
category / token / whitespace nodes] C --> D[mainHeader / fileHeader
insert includes + profile objects] D --> E[functionCount
inject count per function] E --> F[lineCount
inject count per statement] F --> G[mainReport
insert report before return] G --> H[print tree to
p-foo.cpp] ``` ## Key methods - **`read`.** Recursive descent over the XML. An open tag starts a new category subtree and recurses; a close tag ends it; anything else is run through the tokenizer and added as token or whitespace children. - **`print`.** Preorder traversal that emits only the leaf text. Walking the tree and printing the tokens reproduces the original source, which is what makes round-tripping possible. - **`getChild` and `getName`.** Navigation helpers. `getChild` finds a child by tag; `getName` reads a `` node and handles both simple names like `main` and scoped names like `stack::push`. - **`mainHeader` / `fileHeader`.** Insert `#include "profile.hpp"` and the profile object declarations. The main file gets the object definitions; the other files get `extern` declarations. - **`functionCount`.** For each function, constructor, and destructor, push a `profile.count(__LINE__, "name")` call to the front of its body. - **`lineCount`.** Walk the tree and insert a `profile.count(__LINE__)` after each expression statement, skipping categories that should not be counted (the `isStopTag` list: conditions, types, declarations, returns, and so on). - **`mainReport`.** Find `main`, walk back to its `return`, and insert the code that prints each profile object's totals before the function returns. ## The runtime `profile.hpp` / `profile.cpp` is the small counting library that the instrumented code links against. A `profile` object holds a `std::map` keyed by line number (or line plus function name) and bumps the count each time `count` is called. Printing a profile dumps the map. So the profiler injects the calls, and this class tallies them at runtime. ## Build and run ```sh cd profiler make profiler ./profiler foo.cpp.xml # writes p-foo.cpp ``` The first argument must be the file containing `main`; any additional `.cpp.xml` files follow. The repo ships `foo.cpp.xml`, `sort.cpp.xml`, and a few others to try. Under CI the tool builds with `g++ -std=c++11`. ## Notes and limits This is the project where the value-semantics work pays off, since the tree owns a lot of heap nodes and gets copied in the driver's `testCopyAssign`. The transformations make assumptions the comments spell out: one `return` in main, no nested functions, and explicit `{ }` blocks on every construct. Those hold for the sample inputs.