So hi everyone, welcome back. I decided to do another stream relatively quickly after the previous one because the first one where we started porting FlameGraph to Rust got a lot of attention. There were a lot of people excited about the project and so I figured we might as well continue because we only sort of got halfway. So if you remember, FlameGraph is this tool where, let's see, that might be a good example here too but the idea is that you run a performance profile of some program and then you can render this kind of visual representation of where your program is spending its time and how it got there. So the way to read one of these is that here for example, the bash was the function where you're profiling or the program where you're profiling, bash called main, main called main, main called reader loop, reader loop called execute command and here like execute command, you can see it took up 78% of the, basically the time that we measured for. And there are two components to this. So the first one is StackCollapsePerf which we ported last time. So StackCollapsePerf is a program that will take the output of a tool called Perf which is used to basically collect performance profiles. So you would run, ooh, that's not what I meant to do. You would, let's make this a little larger, does streams inferno. Ooh, what even is this diff? Oh, let's stash that. So the idea is that you run a program with Perf record, whatever program you wanna run. Let's get rid of some arguments here to make it clearer. So you do Perf record dash G just includes call graph information. So in addition to knowing which function was run at the time when you sample, you also get to know which function called it. Call graph dwarf includes more additional information and then this is the program you wanna run with any arguments. And the output of this is a binary file called perf.data which is a file that is pretty hard to parse. The format is not well documented but you can run perf script which produces a, it produces output that's meant to be relatively easily parsable. And then you pass that through stack collapse perf and what stack collapse perf will do is basically generate the raw data that's needed to produce these flame graphs. It basically generates call stacks and how many samples there are for each possible stack. And so that's what we did last time is we ported over stack collapse perf but of course the next thing you do is you pipe it through flame graph and that gives you one of these actual images like an SVG. And so the plan for today is to implement flame graph because we already have stack collapse perf. So last time we started this tool called inferno and inferno is the goal at least for the time being is just to port stack collapse perf and flame graph so that we have sort of a self-contained program that can do those things. Since last time we've actually had a bunch of people contribute to the project which is pretty cool. So for example, we now have regression tests so we have all the tests from the flame graph stack collapse tool we have a comparison script that will run benchmarks against both stack collapse perf and this program and compare them using hyperfine. We have the various Java tidying now implemented and we also have some other fixes to tests and there's one big PR that's still outstanding that is basically on adding unenlined support. So this is using adder to line so it's instead of looking at the report from perf just looking at the stack trace that perf gives you instead what we're gonna do is use the debug symbols in the binary to resolve the addresses backwards which gives you more accurate stack results. But this one still has a little bit of way to go but there's been a bunch of cool work from this person Jason R. Henson who basically has been putting it a lot of work to get this PR landed and then that's gonna be really cool when it lands. But today the goal is to generate these. So what we're gonna do is we're gonna look at frameflamegraph.pl and this is the file we're gonna report this time. One thing you'll notice is that because this generates basically a website like it generates an SVG with some inline scripts and such a lot of this code is sort of not Rust related. So what we will probably end up doing is at least for all the JavaScript code we'll just copy that over the way it is because that's not really code that we care about porting. The thing we care more about is generating the actual SVG and that might be pretty tricky. It might be that we end up discarding some options like color schemes and such at least in the first iteration just so we make sure we get something working. For those of you who didn't watch part one you can find part one on YouTube. So if you go back and watch there's a bunch of older videos there but that would be the one to start with if you're interested in what we're currently doing. Okay, so I guess it's about time to get started. I think we still have a bunch of scripts left over from the previous stream that we can use to do this plotting. But what we're gonna start with is in cargoTOML there's gonna be another bin and that's gonna be infernoFlameGraph and FlameGraph. Now another thing you'll notice is I've done a little bit of reorganization in that inferno is now a library and inside that library there's a collapse module that has a perf module because we might implement collapse for other source languages and so all the most of the code that was in the binary that we wrote last time is now in sourceCollapsePerf and then binCollapsePerf pretty much just does parsing command line options and then calls into the library. The other reason for doing this is it means that other if someone else writes something that wants to parse perf script output or... So this means that other people might be able to use the StackCollapsePerf stuff we've written or the FlameGraph stuff we're about to write. There's for example, this thing called endPerf which is a re-implementation of perf and it's also written in Rust and they really want the ability to have you type like endPerf FlameGraph which would be pretty neat and they can do that if we provide basically FlameGraph as a library. So in our case, what we're gonna do is copy over source binCollapsePerf to source binFlameGraph. Oops, that's not what I meant. Like so. And then we'll also start a new file. So in sourcelib, we're gonna add a mod FlameGraph. We might end up with some modules there actually. So let's do a maketer source FlameGraph and then we're gonna have a modrs. This is also, so in the 2018 edition, we don't actually have to have the extra folder. We don't have to have a modrs within the extra folder. We can just have FlameGraph.rs that also have FlameGraph folder. I still kind of like the mod setup so I think we'll keep it like that for now. Okay, so this is where most of the stuff is gonna be and in the binFlameGraph, that is gonna use not this but instead use FlameGraph. Let's just name things the same way for now and then we can clean up that later. So collapsePerf, the options are probably not gonna be anywhere near the same. Let's look at what options there are here. It's a lot of options. I think we're probably gonna ignore all of these for now. Yup, we're gonna not take any to begin with. What that means is we're still gonna have these same, the structure is gonna be the same but there are going to be no options to begin with. We're gonna add them later and then we sort of want this pattern to still be there. Yup, we also kind of don't want this to be called handleFile but and I think we'll still use this, it might be we still want to use this sort of basically state machine to produce the SVG but I'm not sure about that yet. So this is certainly gonna be a handleFile and the binFlameGraph is gonna be called, I guess we're still using structOpt. So it's gonna be this, we're gonna cut out all the help text for now and there are going to be no only inFile, collapsePerf outputFile and that's not gonna be needed. Oh, what is from args? Do we need a from args? No, I don't think so. Actually, it's a good question. What was from args? This is just sort of setting up the initial, from args, really? Oh, bin collapse. Just setting up the initial structure. Oh, great. Okay, so flameGraph bin is gonna do that and it's fine for it to include the options. I guess we can include this here. It's just gonna return empty options because there are no options so far and it's gonna do the same thing as we did for the other one. Great. Oh, browser tops and bottom are fantastic. I do that. So here it's not as important but at work it's actually great because I have a vertical monitor and then the bottom of the screen is easier to reach with my eyes than the top. The top is like farther away. Okay, so I think we now have the base structure. Let's just like see that inferno flameGraph help. Let's just see that it actually runs and we didn't make any really stupid mistakes. Oh, we probably did. Why is this complaining? Source lib, inferno flameGraph. No, that should be right. All right, but this though is missing collapse perf like these things. So not that. And this function's body doesn't return. That's fine. Great. All right, so we now have something like we have a binary that we can start fleshing out. So now the question is what does handle file do? Make this a little bit larger. That is a good question.  Title, I guess we're gonna set after a while. Name attribute file format. What's name atter file? There isn't really a file like that. So where does that come from? File holding function attributes. Name atter. There is no name atter argument. That's documented. That's pretty weird. Okay, so that's a function info by tab then a sequence of tabs separated name value pairs. Oh, so this is to be able to override things in the SVG. So we can probably just ignore that for now. And notes we're also gonna ignore for now. Colors we're gonna ignore for now. SVG functions. So these are things for, these are basically how to sort of set up the various SVG primitives. So if you remember in the gear, let's pull up an actual one of these SVGs.  Like each of these is a rectangle. And so if we go in and inspect this, like each of these just a rectangle that is probably contained within their parent somehow maybe. No, they're just base Gs. Okay, so you'll see, I wonder how the position of this is set. That's another good question. It almost looks like all of these, each of these is a G and they're just sort of there. Sort of hanging around. So I guess it's really the stuff inside, right? Yeah, so each of these is a rectangle, right? That have their position set in a fill and then some text added to them. As what we need is the ability to generate these kinds of things. And so everything that's in this like SVG package function business is just the ability to, I guess this is the SVG header. These are just to include raw SVG content, setting a color, starting a group, right? So this is where each of these Gs is created by one of these start groups. And then that sets the title and what are these H refs? I guess we don't know what those are yet. So it says a bunch of attributes on the G. So that just starts the group. That ends the group with a NG. And then this is what creates the rectangle. This is what creates the text. So this is the stuff that we're gonna need. If you wanna experience your Go, can you compare with Rust performance wise? And doesn't make sense to transport it to Rust. It really depends on what you're doing. I would say that Rust is generally a more performance oriented language than Go is. But often it also doesn't make a difference. Oftentimes the additional speed the Rust gives you is not something you need. Go is a little nicer to program in in the sense that the language is simpler and the constructs are simpler. But it is, like I like Rust because the compiler is strict and you have better types. And so you can make the compiler do more of basically the work of figuring out whether your program is correct. Consider to do a live stream of procedural macros too. We could probably do that, that's a good idea. I'll add it to the ever-growing list. It sounds like what we might want is some kind of library for generating SVGs instead of just like generating these, just using text. Oh hey, SVG composer and parser. Did it a month ago, great. All right, let's take a look at the docs for this. Um, document SVG. Okay, so we can add nodes, that's fine. So what implements node? Group, that seems kind of promising. It seems pretty straightforward, right? And it means that we don't have to generate all of this just by writing out text like the way the Perl version does. A lot more heavyweight than if we just output it ourselves. So yes and no, it is true that it's easier to just print stuff but it also gets you into weird territory with things like encoding.  You need to deal with things like closing tags correctly. The upshot of using our crate for this is that it basically lets us use types instead of strings, which is nice. The other reason why, well, so one reason not to do this is if we print, then we can generate the file on the fly as opposed to just writing it all out at the end. That said though, I don't know that there's a huge advantage to that because you can't really render the SVG until you're done anyway. So I think we're just going to use the SVG crate. That seems fine. So we're going to add a dependency on SVG version 0.5. And then this is going to use SVG. Great. So how does this get started? Vector hash for the name string to pick the same colors for function names across different flame graphs. Well, that's interesting. So it's using the IC. So the way it's generating these then is things that start with similar characters are colored similarly. It's the way I read this. Right. So it takes the name of a function assigning more weight to the early characters. So prefixes of the function so that you will get consistent colors even if the thing moves around in the plot. That seems fine. Color is just different color palettes. Multi-palettes I think we're going to ignore for now. That is sort of an additional feature that is really cool. Like it basically lets you, if you have a thing like this, if some of these happen in the kernel, then those would be colored in a different color. So they're easy to distinguish. You totally can render the SVG while you build it. If you manage to compile this to Wasm. Yeah, but I don't think it matters. Okay. So we're going to ignore this color business. I mean, we are going to have to deal with actual colors, but I don't think we need quite this much complication at least to begin with. So I'm going to ignore that for now. Right. Palette.   It writes out a palette dot map. Why does it do this? If palette. Oh, I see. CP. I see. So it can like remember what palette, which colors it used for which functions in the past and then restore that later. So it's also like an additional feature we could add later. Doesn't seem important for right now. For the people in YouTube looking for the Twitch stream, there's no software development tag, but English IRL creative under science and technology. Yeah. Kind of annoying. Okay. So read play. So this business is really just dealing with persistent color templates. Flow merges to stacks. Okay. This seems like something we're going to have to look at eventually, but let's I want to get to the part that parses the file first. And then we can look at where flow is called reverse if needed. That's weird. Okay. So this is where we're just reading in. Just reading in the input, which is, so this is the output of stack collapse perf. So if you remember the output from stack collapse perf looks like folded dot log basically looks like this. So on every line, there is a particular call stack. So this is API API called course slice. I guess let's find a longer one. The thread client zero called some function, which called TCP stream read. And that this call stack appeared in three samples is the way to read this. Similarly, this call stack appeared 56 times. So that is the entire structure of this file. There's nothing else in it. So what is stack reverse? I mean, I assume it reverses the stack, but why reverse? Generate stack reversed flame graph. Let's just not support that for now, either. That seems like an easier thing to add later. In some sense, what I want is I want the program to work with no arguments. And then we can deal with adding features later. Right. This seems like basically all is doing is splitting by semicolon and then inverting and then doing whatever other thing this is doing. It's unclear. So really this is just reading in all the lines. And then it sorts the data. That's interesting because the data is already sorted. This should not be necessary. If you remember from here, stack collapse perf. The last thing it does is it sorts. So I don't think it's necessary to sort here. Although I guess maybe actually, no, it is. It is because of this, right? If you reverse the stack, then you do have to sort again. All right. So we're this is still that if you consider there being no arguments, then this is still just iterating through every line of the file. And then we parse out. I mean, this is parsing the line, but it sort of in a weird way. Everything up to some number of space. Why is this is that complicated? Some number of digits followed by this is a positive look behind assertion. I wonder why that's there. I don't think that should be necessary. Let's look at the blame for this. So blame is really handy for these things because we can try to find the commit where that was introduced and see why it is that way. This specifically, what did this change? Oh, that just removed a white space. That's unhelpful. Let's see where did it go here? So this changed. There may be an extra samples column for differentials. Why is that comment not in the original? Why was that comment removed? Oh, maybe it's further up. What are differentials then? Interesting. Okay, so this is really saying that let's see. Can you do something like cat file one, file two, flame graphs? So they wouldn't be sorted either. That's true. You could combine flame graphs that way too. So that's another reason to sort them. Yeah, I don't know why it's sorted in the first place. I don't know why the output of stack collapse perf is sorted. I don't think it needs to be. Although that sort should be pretty fast anyway. But you're right. I don't think that should be necessary. So it might be that we can just get rid of that. Although then it would become pretty hard to basically run our regression tests, because our output would just be nothing like the real output. What are differentials? Well, so that's what we're trying to figure out. It seems like, this is no pull request document in this, but colored based on higher samples and smaller samples, which are the before profile? Go by what will happen. I mean, this seems like maybe it's useful, but it's hard to say. Okay, this sounds like a commit that we should keep track of for later. And then just ignore this for now. Oh, see here it's documented. Interesting. Yes, I think what we're going to do is just not parse the extra sample column. Why is there a dot? Why are they dots separated? And then this is one of the things we can list as an issue of enhancement to support differentials. Okay, so let's ignore that line for now and just assume that what it really picks out is the stack, which is everything up to the number at the end and the number at the end, which is going to go in samples. And then we're going to ignore lines that don't match. That's fine. Oh, so now the commentator here. Okay, fine. So we didn't actually need to go through that. Oh, why does this match again? Oh, wait, this is doing something else. So this is matching on the whole line. This is matching on the stack, which is the stuff with the last column removed. So why is this here? That is a very good question. Everything followed by maybe a space, followed by digits, followed by dot and numbers. Oh, so this is just ignoring fractionals, I think is what this is doing. It's saying that if the sample count is fractional, then match against it, but don't include it in the match. I mean, let's double check, but I think that's what it's doing. Yeah, non-capturing group. It's really weird to decide to do this with non-capturing groups, but that's what it does. So this is just saying if the sample count is fractional, then ignore the fractional part, which we can do much easier than this. Okay, so really, this is, okay, this is kind of weird. So really, this is an R-split, well, it's sort of a, it's a split white space from the back. You pop once. Actually, you could just R-split N2 space twice, although we might want to use split white space, it's not clear. And then you look at the last group and that better be digits. And if it's fractional, then discard the fractional part. And then you do, if you do it again, then you get the differentials, but we're just going to ignore differentials for now. So this would be false, so we can ignore everything that's in there and that's not defined. So samples two and delta are both going to be on def. For chain graphs, we're going to ignore chain graphs. Merge frames and populate node. Okay, so there's this flow business. So last is going to be set equal to whatever flow returns and passed into the next one. So we're sort of recursing on last. We're giving a list that is an empty element and then stack split by semicolons. Time, where does time come from? Does time come from? Time is zero. Oh, interesting. Oh, it's probably, okay, so what I'm guessing this is doing is it's looking for things that share a common prefix. Right? Doesn't describe. The non-capturing group is still in the capture. Yeah, the question is, why is this a non-capturing group? Like why use a non-capturing group here? I don't think it's important though. I think, because like this part is just matching fractionals and this is a non-capturing group. And so this thing is matching an optional fractional part. I just don't understand why it has to be non-capturing. That doesn't seem important. I guess it's because otherwise this would return a three-tuple but I think you could just ignore that argument. Okay, so my guess is this flow business is just gonna look for things that share common ancestors. Time is just gonna be how many samples we've seen so far. And delta is undefined. Okay, so now let's look at what flow does. Merge is two stacks. Great. Okay, these are just the arguments. That's fine. Oh man, Pearl. 1A. So it's looping through the last stack frame. And if, and seeing whether there's a shared prefix. Yep, it's just iterating over both and seeing the first point at which they diverge. And then it's looking at the part from where the previous thing ended greater than or equal to the same. It goes from where the previous thing ended and goes backwards towards where they diverge. Oh man, that's weird. Last I. New idea is constructed for funk depth E time. So I is the depth of the call stack. Sure. So I guess that's like how far up it's going to be. So this is constructing what is the shared prefix and how deep is it in the overall call graph? Like how many stack frames are there? Because you need to know how far from the bottom of the, basically of the SVG to draw that square. I do not have an experience with the Vulkan API. Sorry. E time. So node with that followed by V. What's V? V is time. V is just the number of samples you've seen so far. So that's guaranteed to be unique. Yes. So that sample time, I guess, is where, what is temp? So we've got temp. That's going to be set down here. Okay. So I guess we'll have to figure out what this line means. We're not using Delta's. So that's fine. Oh, the, this language is Pearl. Pearl is a, it's a language that's very good for string processing and pretty painful to read for, well, I mean for string processing as well, but just generally pretty painful to read. Okay. So this is a unique identifier for why does it need last? Like Len A, I think this means that it's only processing a given line when it gets to the next one because it has to decide how deep that thing is going to be. Okay. So this is iterating over all the frames after the shared prefix. And this is iterating over the things. Oh, this is iterating over all the things that are only in A. And these is all the things that are only in B. So this is the, these are the things that are only in last. And these are the things that are only in this. Why is this one iterating in reverse though? I don't know. It doesn't seem to be a good reason for that. Like why does this not iterate from Len same to Len A with I plus plus? Why is that important? That is, why is it important that it doesn't? For the deltas maybe? I guess K is not unique. Actually K will be unique in this particular instance because the I is the depth and that will differ. Yeah. So I don't know why it's iterating in this order, especially because the iteration order of hash is also not known. So I don't think that should matter. I think we, we can reverse this. And then iterating over the things that are in this, constructing that. Okay. So it's constructing the same key. Okay. So temp is really the samples, the unique things from the previous line we iterated over. Right. Because remember, this is this, this is lost. Right. And so next time this is called, this is going to look at this is basically from last as well. This is where last differed from its last. Oh man. This code is funky. Burn this. Why does it return this? Oh, because it's at last equal to this. Okay. That's fine. So really this node key is what we need to look at. Seems like node is where, because that's the only, if you look at it, that's only the only real output of this function is this map node. It's like modifying this map node. And it's really just constructing node so that it has, so that it contains every, it contains one entry for every distinct call chain, every distinct item, I think. Oh, I see what it's doing. Okay. So I think, I think what it's doing is the following for it. So what's a good example of this? Let's look at execute simple command. So we're going to have a bunch of stack frames, one that contains like execute simple command, execute built in function, clean up redirects, one that connects execute simple command, execute built in function, do redirections, execute simple command, execute built in or function expand words, etc. But we only want one entry for execute built in function, even though it appears in multiple stacks, right? And so I think that's what this code is doing. It's adding things that were unique. I think it's basically just making sure that it only adds each thing ones at the level at which it's supposed to be. I don't know why this, why the time is included though. We'll have to figure that out. And this really just sets, it just sets the S time. So yes, okay, let's go back to here and see what it does with the results. So after it's flowed all of the stacks, then what does it do? Okay, that's fine. That we can just ignore. Time max. Oh, this is to say, don't parse times, we'll have to figure out what that does. Right. So now it's iterating over node, which is one entry for every function that appears at a given depth. And it has to be distinct because you could imagine that like here and here, both of these were due redirection internal. So they have the same name and the same depth. So you do need to distinguish them and they do that by samples. And this is doing some kind of pruning. So we'll probably have to do that. Just getting rid of things that are too narrow to show. Draw the canvas. That's fine. We don't particularly care about that.  We don't really care about that. Down here.  This is what we care about. All right. So for each node, there's an ID and a node. What is node there? What is the value within a node? That's a good question. I guess that's brings us back to flow. So the value within each node is, it's a hash that contains the value S time. May contain Delta. Node is just empty up here. Is anything else modifying nodes? I don't think so. So I think it really is just a hash. So the only thing that's really in there is S time. So node is really just, yeah, it just contains S time and Delta. Interesting. So X1 is X pad plus start time. Oh, I wonder. So it's considering the sample time. So what we pass in as the time argument is actually what's going to determine the width of each bar. So start time is going to be like here and end time is going to be around here. How does it arrange for that to be the case? I wonder, why is that the case? How do we compute this time? So the time is computed by, the time is computed by just adding the number of samples in the line. Why does that work? If it never gets reset, then why wouldn't you? Oh, it's probably because of the sort. So remember the iteration order here is basically going to be like a depth first search. So you're going to be moving through this graph in this order, right, in sort of that order, or I guess I don't know how the screen looks, but sort of from bottom left to top right. And that is really going to be the start time. The question is, how does it determine the end time? Oh, that's so clever. Okay, this flow function is very clever. It is very dense, but very clever. Oh, I think I need to draw this actually. That might help. Don't I have a draw program? What's it called? Paint? There we go. Hey, I get to bring out my painting thing again. That's good. So I think here's what's going on. Let's see if this works. Where's my, great. So let's do a new discard. So when we walk through the file, that's going to have something like A, B, C, like three. Then it's going to have A, B, I guess D, four. It's going to have A, C maybe like eight. Of course, now the end time of B should be seven, right? Should be three plus four because B lost for this entire duration and the end time of A should be 15, right? This should be seven and this should be 15. So what it's going to do is when it encounters this stack, this first stack, right? When it encounters that, oh, that eraser is terrible. There are a way to, I feel like I had a button for this, but maybe not. Oh, like this maybe. There we go. Great, all right. That's kind of an outfall, but all right. Oops, and now the colors are wrong. And what's going to happen is when it's looking at this first stack, it's comparing it against the empty stack, right? Because that's what we told it to do. So at first, oh man, I don't remember. Marker small maybe? Marker fat, great. That's sure, whatever. It's not important. So initially it's going to start with an empty frame up here and then it's going to compare that to ABC. It's going to iterate through, so remember it first goes over last. So it's going to iterate through nothing and do nothing. Then it's going to iterate over new and it's going to set in its like node map. It's going to set everything that, what was it? Everything that follows where they are the same. So first it's going to set A depth one to, and then the current time, which was, right? That is what it sets, I think. I feel like it flow. So it's S time to V and V is the time that's passed in, which is three. So it's going to set three. And it's going to set that to, oh sorry, it sets that in temp. Right, sorry. So there's also temp. So temp is going to have A one is three. Right, and it doesn't have anything in here yet. What, really? Why is there not a better marker for erasing? So in temp, it's going to set A one to three. And in node, it hasn't set anything yet, because it only sets node when it's iterating over things from the previous one. So that's all it's really going to know, I guess. And then it's going to set B, it's going to set B just B, is that all it sets? This I, yeah, so just B. So it's going to set B at depth two is also going to be set equal to three. And C at depth three is going to be set equal to three. Right, and then on the next iteration, this is last and this is this. Right, so it looks at the common prefix, which is here. That's the common prefix. And it looks at things that were, so it first looks at things that were in last, but are not in this. So it's going to look at C. And it iterates backwards over these. Right, so it's going to look at C. Up here. Right. And it's going to set in node, it's going to set C three with the current time as the differentiator. So C at depth three was the current time, which is three plus four, so seven. It's going to set that to be what it deletes from time with the entry C three, which is three, so it's going to be three. Then it iterates over this in forward order, and now this is deleted. Right, then it moves in forward order on this, which is going to be just D, so it's going to set D at depth three is going to be equal to the current time, which is seven. Right, three plus four is the current time. And then now this is done. This becomes last, this becomes this, and the common denominator is now this. So it's now going to walk backwards through B and D, and forwards through C. All right, so it looks at D, and it constructs D at depth four with current time, which is 15, is equal to delete, sorry, D was, sorry, this should be three. Right, A, B, D, deletes D three, which is seven, so it's set this to seven, and then deletes this. And then it gets to B, right, B is the next thing in here, and so it's going to set B at depth two with n time 15. Is that right? Yep, is equal to delete B two, B two here was three, right, three. And then it's going to move forwards through this, which is going to add here C two is going to be equal to 15. And then it's going to move down to this is here, this is no longer last, this is now last. And then it's going to move backwards through these and forward through nothing. So backwards through AC is going to be set C at depth two with n time. Actually, I might be off on all these, these should be, I think these should all be down by three. That's not important. So this, because this has to be 15, this has to be seven, and this has to be three. I think this is going to be three, seven, seven, 15. So C two 15 is going to be delete delete C two, which is going to be 15. Yeah, so these are also wrong. This should be zero, should be zero, should be zero, should be three, this should be seven. So this is equal to seven. And then it gets to a, so it sets a depth one time is 15 is equal to delete a one, which is zero, like so. And now if you look at it, a at depth one is going to be from zero to 15, which is the entire width. C at depth two is going to be from seven to 15. B at depth two is going to be from three to seven, right? I guess these should also be decreased by three. I just messed up my counting of time. So for every, for each one, the star time is what they're set to, and the end time is where they, the time at which we decided to add this to node. I don't know if this was, this was kind of weird to follow and I'm still not sure why it works, but it does seem like it works. Okay, so I think what we do is just port that and try to document it as best as we can. Let's see, because I guess down in the SVG generation, all it then really has to do is just compute the absolute coordinates of those times, right? Which is what it does here, x pad plus a time times width per time. And then down here, right? So all samples that just generates the very bottom bar and for anything else. Yeah, so this is the kind of stuff we don't have to do for use in SVG library. And then that just prints what's going to go, what the hover text is going to be for each one. So you see when I hover over these down here, there's like text that shows how many samples and percentage. So that's going to be, that's what this generates. This is the info. And then it generates what we're going to have the different attributes B for the node that we create. So this is also pretty straightforward in our, in the SVG generation library. And you start that group, then you set the color, and then you create a filled rectangle. So this is the, in the SVG, this is the rect, right? So that has to have a color set for it. And the x and the y's we've computed. I wonder what this is doing. Oh, this is saying, if there is no room in it, then don't put any text in it, right? So I wonder how it computes cars. Oh, it just like looks at the font width. Okay, so it tries to guess how many characters it can fit inside the box it generates. And then if it thinks it can fit at least three characters, then it does, then it generates a short potentially shortened version of the text. Right. So this is like add dot dot if necessary, add the strings ends the group. All right. Yeah, these kind of like hidden gems and traversal are really I wonder how Brendan arrived at this, like this, it's magic, it's like black magic to me that this works, but it is really cool. All right, so I think we're at a place where we can now sort of start doing this without any features or anything just sort of to get started. What we're going to do is we're going to create a new SVG document, right? So this is going to be SVG is just document new, document new, new yep. It's probably gonna have to be mutable. And at the end, I guess, what can we do with document? Can we write a document to a writer? Great. So at the end, what we're going to do is SVG write to our writer, the SVG. And that's what is that going to produce IO result? Perfect, because that's already what we're returning. Okay. And my guess is that just like in collapse perf, there's like a decent amount of overlap in that we want to be looping over lines. But I think the looping is a little different. What we'll probably do here is the same thing as we did when we originally wrote the stack collapse perf port, which is we'll start by sticking everything in one file like keep the main loop and then we can factor it out later. Where did you switch to rust from? Oh, all sorts of things. I did a lot of go before. I've also done a lot of web programming.  But I think rust is sort of my the language I've at this point spent the most time with. I've written a lot of C++ did not like it. Okay, so the kind of stuff we have to keep track of, it seems like is mostly like we need last, right? We need, I guess let's call them the same for the time being. And then we can give them better names after a while. So this is going to be a hash map. Similarly, node is going to be a hash map state we don't have. If we're at the end of the file, then we break the loop. We don't have to ignore comment lines. We do have to parse the line though. I guess we're going to trim the line. That seems fine. If the line is empty, then I think we're just going to continue. Remember, the reason we have this here is in the old file empty lines denoted the end of an event, whereas that's not the case here. And now we want to do the parsing to try to figure out whether it's a sample line. Remember, we're now just ignoring all of this business of being able to reverse it and sort it. I guess we may have to cat multiple files. So that's a little awkward. I'm just going to ignore this for now and have it work have it only work for one file for now. So I guess up here is going to be something like a to do technically need to pre process lines. Reverse stacks in case of multi file. So now we have to parse the line and we're going to not parse it the same way they do. Instead, we're going to say fields is going to be line, I guess, split white space. It's a little awkward because split white space doesn't give you the index. So we wouldn't know where to trim to, which is why I think what we actually want is R split n. Although that's going to require that these separators only a single space, which I think we know is the case. It sort of depends how help oops, that's not the one I wanted at all collapse. What do we write out for this? I think we just write a space. Where do we actually write out the after event, I guess entries? No, it's entries sort here. Yeah, we do just write out a single space. I guess the question is, how much do we want? Actually, here's what we could do. We could just do our find instead. We're going to our find space. Yeah, that's what we're going to do. And then I guess what we want is samples. Any reason use hash map a default over new, but not for strings? No, not really. Arguably, it should be consistent. You're right. I sort of like using default over new because, well, usually what I do when I'm making various structs is just to default default, because it means that I only have to change the type in the struct definition, not in the implementations, because the type inference figures out which default to use. And that is more often hash maps than strings. But in this case, I think it's fine to just use new. I've been wondering how you get your search part to the bottom of your browser. It's a nightly feature, something. No, it's actually in Firefox, you can write custom CSS for your browser. So there's a, I think I have it just somewhere that does this somewhere, this business.  Is a norm adapted for this kind of parsing? Yeah, so someone asked this in the previous stream as well. We could probably use norm for this parsing. We probably could. One difference, though, is I guess we could write a parser for the line. I don't feel very strongly about that. I guess I'd be fine with it. In this case, the parsing is so simple, though, right? It's just like we're splitting by whitespace. It seems kind of weird to pull in all of norm just to do that, because we're not really parsing a full language, right? They're very simple lines. So we're going to do is if let's, I guess, samples, I just fields dot next. Yeah, I know. I miss opera too, although opera was also bad in many ways. Then, then, I guess, what we really want here then is sample, then we want samples is going to be rewrite this better later, but it's sort of going to be line samples I dot dot, and we want to parse that. We want to parse that as a U size. However, this is where it gets tricky with the fractionals. So I think actually what we'll do is this strip fraction. Actually, I don't know what the U size parse does, whether it just ignores trailing fractionals. What does from string radix do? Because it sounds like this would just panic if that's unhelpful. Give me the actual source of this. Still pretty unhelpful. There. Okay, so it's the leading plus or minus digits is positive. Yeah, it if you if it got a dot, it would just get sad. So we do actually have to strip the fractional part is samples. More of a trade of not pulling that many dependencies over simplicity of the problem. I think it's a combination. So you could totally pull in I don't know that that makes writing this code simpler, because then you have to figure out how to write the correct nom for parsing this line. If you know nom, that's not an issue. I have written some nom in the past, but I don't know that I would I don't know that it would be any easier for me to write the correct nom code. So a lot of work to make your VIM work like yours. I don't think it's that much work. I mean, there's a lot of stuff you can do to configure editor and especially an editor like VIM. But that's the same for any editor. It will take you a while to get used to that's about it. So the reason I'm hesitating here is because it could be that the sample is like, so imagine a line that looks like this, if we strip the fractional part, then the fractional part will still be wrong, right? Like this is should not be parsed as a sample line. But I'm going to ignore that for the time being and just sort of assume that if the line has a if the last column of the line has a space in it that we're fine as sorry has a starts with a digit followed by potentially a dot, then samples is going to be samples.i. And then if samples. Isn't there an I guess what we want to do is match samples.parse use size. And if it's okay, then we get and samples. If it is any kind of error, then what we really want to do here is just continue. I guess what we may want to do is sort of include this ignored bit, right? So they have a in the pearl version, there's a number of ignored lines. And we want to keep track of that too. I think in the in this code, it doesn't really print anything, it just counts the fact that it ignored it, right? And then while we're at it, we also want to set line to be everything up to samples.i dot trim and move samples from line and then give out the sample count. And if it's none, then I think we just continue and I guess ignored plus equals one. Of course, we could also use regular expressions ourselves here. So in in Stacklabs Perf remember what we started doing was we we started writing the regular expressions and then we profiled it and we figured out whether it was slow and the things that were slow, we converted from regular expressions to inline parsing. And we could do the same here. This parsing was just so trivial that it's like not not worth it. Although I say that and I still written a screen full of text. So unclear. Okay, so at that point, we have samples and we have stack is going to be equal to line. And of course, here, we're going to have the same thing of if okay, these we don't use. If if line is now empty, then we continue because it means that we didn't have a stack. And then we're just going to rename line to stack because it's easier to work with that. And I guess we could call this samples instead of n samples just to match the pro code. But I would rather have it called n samples. Samples to me is a vector of samples and sample is the number of samples. Okay, so this is where we get to flow. And so this does something like last is equal to I don't think this needs to return anything because flow does not modify this. Right? Unless I'm mistaken. No, it just returns this. So I think really what I want here is just let's call it flow because why not? We might want this to be something like self dot flow. Eventually, we probably want to better name them flow too. But this is going to pass in mute temp, mute node, last, this. And this is just stack. Right? It's a little annoying that we're going to have to keep that string around. It's going to be an extra allocation, but it's probably fine. It only needs read read only access to to these. And what else it needs n samples. Right. And then we're going to set last is equal to stack. I like I prefer that to having flow like return this. It just seems odd. When that's really what it's doing. It's easier to have that explicit here. Of course, this isn't going to work because of types. But let's ignore that for a second. And then we're going to have time. So this is going to have to also have let me time is zero. We're going to say time plus equals n samples. And then there's also after this loop, we're going to have to flow this with I don't think I want this splitting to happen in the caller. Actually, what does happen if you if you split by semicolon and an empty string, what do you get in rust? It's a good question. I guess empty string split by semicolon dot collect back to see what that gives us. Great gives one entry, which I think is what we want. So this should be last and nothing. And this should not be n samples. It should be time. Right. So I got that right. Yeah, it should be the previous value. It should be time. And then this should also be time. Great. And then we're going to go into like draw the SVG to do draw the SVG. So now we need this flow function. And flow is going to take temp. It's going to be a hash map of indeterminate types for now. Node, which is going to be also something we don't quite know yet. What else it takes last, which is a string, it takes this, which is a string, and it takes time, which is a U size. And it doesn't return anything. Question is, what are these types? I think they're all just to U size, right? Yeah, because we don't have this depth thing. So for now, they're all going to be just to U size. And what is the key? I think the key is just like this unique string is the way they do it. But we don't really need it to be a unique string, I think. Well, it's actually going to be a string regardless, because it has to include the function name. So because of that, it might as well just be a string. The alternative, of course, would be that we do something like a struct. In fact, maybe we want to do this so we don't have to parse against later. So there's going to be something like a function at depth, which is going to have a function, which is going to be, I think it has to be a string, because we're going to keep it around for a while. Oh, so this is actually the real reason that we might want to slurp in the entire file in one, and then do like the sorting and whatnot, because all of these can be stars. So this would also be would also let us only operate on stir, which would be nice. Like last could then, for example, continue to be a pointer into the string even after we moved on to the next line, which is not true with the current way we're parsing. But that's an adaptation we can make later. So depth is going to be U size. This is going to derive this is going to derive debug partial eek, eek and hash. This is going to be function depth, and an optional time. I guess this is going to be something like that's going to be an option U size. So remember how in in this map we need this extra unique identifier? Actually, I guess that's end time. End time. But if we're using it for temp, then temp doesn't have an end time. And now these are all going to be from function at depth to use size. Right? What is it complaining about? Like why is it it? Why is it not formatting my file here? That's better. Alright, so now flow takes those arguments. And again, I think flow is going to be a function on a self where the self contains Tempen node. And so this the arguments are just going to be last this in time might not even be last last might also be stored in self is going to take something like mute self this in time. Alright, so what does flow do? Well, from or rather not from memory. It looks for the first place of which two arrays differ. Right? So specifically, it's going to look for last is going to be last dot split on semicolon. This is going to be this dot split on semicolon. And I think we sort of want peak. Oh, really? Peekable? It's never a function on iterator. Yeah, okay, great. So peekable lets you have an iterator where you can look at the next element without taking it. And the reason we kind of want that is because it means that that we can just continue using the iterator in both of these cases. I still don't know why this has to walk in reverse. That is still really weird to me because this key has to be unique for each of these iterations. Like K has to be unique for each thing in this iteration. So the iteration order here should just like not matter. Unless I'm missing something, but I don't see what that would be. Because now what we can do is while last dot peak is equal to this dot peak, last dot next, and this dot next. Remove common prefix. Right. So that's what this loop is doing. It's just figuring out how far they are the same. And there's no it's never looking at the things that are the same. See, that's a good question. Lensame. Lensame. Yeah, so they're not looking at the index of the last thing that is the same, they're looking at one pass. Maybe because of how the wrote the code was thinking, he wrote it in reverse for easier reasoning, but it doesn't actually matter. It could be, but this one is written forward. So why is this one written backward? I don't have a good answer to that. I guess maybe we'll find out when we try to test this code. But and now what we're going to do is for for funk in. So Len a is lost. We're going to make a key that is going to be a function at depth. And it's going to be the function. Is that what I called it? Function. So the function is going to be funk. We're probably going to need some like two strings here, but let's ignore that for now. Depth is going to be, oh, we need to keep track of the depth. So this is going to be like a shared depth. So shared depth can't spell dot enumerate. So depth is going to be shared depth plus one. Sorry, plus I, yep. In that while loop would be in that while it would be true if both return none. Yes, you are right. If last dot next dot is none, then break move along prefix iterator. They must both be none. So we're done. So let's stop looping. That's a good catch. So that's the depth. And then I guess there's no, all right, and the V right. So the, what did we say here? End time. So end time, we're going to set to none. First, because we need to look up the S time. And will that always be there? We guaranteed that it's there. I think we're guaranteed that it's there. So start time is going to be temp dot remove key. Expect did not have start time for at depth. This is an unreachable. At least we think that that's the case. Start time for and that's going to be key. And then we're going to insert into node, right? So we're going to do node dot insert. Well, we're going to do key dot end time is equal to some V. And V is really time. And then we're going to insert. Why does this need to be a map even? I don't think this needs to be a map. I think this can just be a vector. I think it matters that it's keyed. Because we just iterate over it anyway, right? Well, maybe we prune without looking at the time. But I think that's the same. I think this can just be a Yeah, exactly. Like I think it's just a vector. So I think let's try to see if we can get away with node being a vector. And then I guess if we want to be really nice in terms of time, just that we're going to say, timed function of depth, and that's going to be item is going to be function of depth. And there's going to be a start time, which is going to be a U size.  And this is going to be a timed function of depth. This is going to be a function of depth look up by key I guess function of depth is a little bit of a weird name for this but we're then gonna set I guess key for the time being it's gonna be a timed function of depth it's gonna have items gonna be key start time and end time is time and then we're gonna push if we iterate the unique postfix in reverse couldn't temp be a stack that's a good question I'm gonna go with maybe and then I'm gonna leave it as a map and then we get we can always optimize it later right if it turns out to be an issue why is it complaining about this oh right and what is this right this has to be to string this unreachable is complaining about something doesn't implement display that's because it has to be debug and and start time great and then we have to do the same for the other loop which is gonna be I funk in this thought enumerate and that's gonna be also let keys function at depth and that's gonna be functions gonna be fun to string it's a little unfortunate all these two strings but I guess we'll we'll just have to live with it depth is gonna be share depth plus I and then we're gonna say temp insert key as equal to time and I guess expect start time already registered for frame I don't think it needs to be an RC string I think you can actually just be string references I think the trick here is gonna be to ingest the entire file into one large string and then only work on slices into that large string that way they can all be stars there's no need for a string anywhere so this would all be not quite allocation free because the map and the VECA or allocations but none of the strings would allocate I think it's the it's gonna eventually be the trick okay so you're gonna love this to do is are great all right yeah if we have to sort we need all the lines anyway and especially if the so the other thing here is for the stack reversal you also need them you actually need to ingest the entire thing and resorted sadly no method next for option you size because it doesn't have to be next and that also means that this can just be this is great expected use right this has to continue great and of course now we decided that node was gonna be a VEC right so here's what I think I think this is what I really want to do I want the type inference to take care of this business I don't know whether will but that's what I wanted to do this is sadly also gonna have to be to string okay great so now in theory at least we have this all working so what we can do now is print out all of note and run that on I guess perf data self curl folded and see what happens empty you say that doesn't sound like what we wanted to happen apparently our output is empty okay that seems bad what about temp temp is also empty so this suggests to me that we're failing to parse the line so I guess down here if ignored not equal to zero then E print line what's the error prints let's just make sure we're as compatible as possible huh and watch this say like all of them 684 lines great so something ain't right I guess here we want something like print line debugging it's the best thing in the world maybe found sample at in there samples I line 286 I mean that seems kind of sensible there oh you're right I could use debug it's true oh it's including the space that's why hey it did something and crashed did not have start time for function empty oh oh I guess we sort of need to seed it with the fact that the empty stack started at time zero so we're gonna do that right up here and say temp insert function at depth it's gonna be I guess function is gonna be string new so the empty string and depth is gonna be zero and now it's gonna complain about this start time already registered for frame so you're saying I should not have this huh let's see did not have start time I guess what we really want here is just a trace flow right so what we want is stored temp for time this and then up here we want ending frame like so and then frame yeah I know debug is nice the biggest problem I have with it is like I could do like debug here I guess I could debug here maybe that is better I'm like that causes my output to be more verbose than I sort of want it and it also means that I can't include context like the time at zero ending frame that's so silly this is why all of the this is why it passes in this empty thing at the beginning because it needs to match that against the empty thing it's gonna encounter in and that's only there for this not for last I mean I guess I could chain this I could say I'll do the same thing makes me a little sad but did not have start why would it why would it think it needs to have a start we're actually so by doing this trick if you get the empty string you're gonna iterate over two empty strings with the sack hmm the alternative here would that be to have flow just take iterators instead which is fine I mean I guess we could do that it's like a little sad but it'll be fine right this will be li this will be ti and then down here when we call flow then I guess what we can do is if because it sort of turns into if last is empty then this that's gonna be this regardless so this is gonna be just iter ones empty and this is gonna be I guess this doesn't need to be the peekable can happen inside here last dot peekable right so this doesn't need the peekable part but it is gonna have to be this which is pretty awkward not gonna lie cannot find oh this should be stack compared to why is that needed at all why can't we just do unwrap or on the remove I guess it needs to know the start and end of the empty entry which is basically gonna be turned into all right so down here somewhere it's probably a here if funk is equal to yep so at the root then you replace it with all so that's we're gonna end up being so that's why so let's see inject empty first level stack frame to capture all right so so if last is empty then we want to I guess need to special case this because otherwise it or this plus split something sense right that's why we need to special case this otherwise you get two of them all right let's try that stir is not an iterator well I agree with that but why is it claiming that I need stirred to be an iterator is not an iterator 153 that is true you are correct this should be it or once this should be last which is gonna be this which is gonna be and that's gonna be last and then I need an explicit lifetime for the return strings but I sort of want to say that it doesn't matter I sort of want this but I don't know if it's gonna let me do that yeah fine feel like the inference should be able to take care of this cuz any lifetime is fine cuz we're not returning anything now what stored time for time zero function in front of collapsed to how is that the first thing that happened I think we need to I think we need some more instrumentation here we're gonna do something where's the call to flow we're gonna have flow of this sorry this this this and that's gonna be I guess here actually it's gonna be empty string stack and time this is gonna be flow of last stack in time and this is gonna be flow of I'm actually gonna special case this just so we can tell them apart and same with this one so that's gonna be last nothing in time what do we know I've been never used oh yes so the first thing it does is call flow on an empty previous with that stuff and zero start time already registered for frame oh did I not remove the thing I put up here no I did shouldn't that be empty did I like invert my condition or something oh sorry this should be none oh yeah I have inverted this this should be if it's if it's some it's a problem great so this should be this I guess or just if let no no not this one this is not inverted the other one is inverted this one down here this should be we should expect that it is none not expect that it's some is there a one option is there a like I want the opposite of expect I won't like expect none there might not be one that's too bad so I guess if let if let some start time it's equal to that then that's more like it okay great try that alright right so so this should give us a better idea of what's going on as well because now we actually get basically the drawing I did we're now getting the steps for that printed out right so it's gonna store stored in temp for time zeros at the very beginning starts all of these functions shouldn't that also include blank no blank is shared right blank is shared so it's not gonna be pushed and then it flows that and to the next one where's the comma where is the argument separator my blind I'm probably blind well there is one there somewhere I just can't find it hmm do do I not print up both the arguments I did I'm pretty sure I do top line after entry thank you someone spotted it better than me there we go there it is thanks good catch okay so this is last and this is so now at time one I should probably print the line as well shouldn't I okay but the time there is one so at time one it should end stack why because stack is not shared between this and this because this one is inferno collapse stack this inferno collapse start so stack does end a time one that is true and therefore it creates an entry in our node that is function of depth stack at two right one two zero is the all start time zero and time one at one ending frame libc read right which is also entered in there for time zero and then it panicked did not have start time for libc read depth three okay so the depth here is wrong the depth there is wrong function of depth is wrong it's probably because I'm adding one instead of adding I yep how about that who it ran to the end that's neat well I mean it seems to be doing some things great I'm gonna just take all these e-prints and comment them out because actually I'm gonna keep the calls to flow I think those are gonna be handy we're gonna ignore these though we're gonna ignore that we're gonna ignore that because they are verbose but I think the calls to flow we can oh and debug see I can't just comment out debug either that's a little sad I actually said add I but wrote plus one great good job me oh these are too annoying that's too bad alright so now at least in theory we believe that we have the correct contents in node and so now we've done all of so we're now like here basically right and I guess here if time is zero so if there are no samples then we do wherever this business is which is print out this this sure does some stuff header what does header do so it's an SVG function they have up here somewhere I see seems fine so there's always a header so I guess what we'll do is SVG and now we need to look at the SVG docs how do I create a comment is really the node element yeah really can I not inject comments that's kind of awkward that seems awkward really I can't append a sign well then a stop element no it's not what I want really are none of these comments I guess I could inject a script but I guess this should probably be a comment well then I guess what we're gonna do is we're gonna do let's go back to this so a document is an SVG and SVG has add and set so we're gonna do add SVG nodes right node description new node element description new and what is supposed to be under a desk so I guess probably visible text and we're gonna do something like header dot add and we're gonna have to figure out how to add text so an easy way to add text or the only annoying ways to add text can I like can I add just like text directly because that would be great what implements node does text implement node text node sure add SVG node text new this and then this to break up that line a little and then we add a header variable does not need to be mutable I feel like that's a lie I think it does have to be mutable it's lying to me use of move value header header was moved here really you're telling me that if I have a node like description and I try to add to it it consumes itself oh it's for easy construction it's so that I can do SVG add this dot add this it's so that I can do that and s see that's just annoying though there's also node which has a sign append a child node so I guess here I can use append is probably than what I want I'll have to import this trait that seems fine great we successfully made an SVG well it's sort of a builder pattern but it's like a little bit weird that it that it doesn't take mute self and return mute self there's a little bit more common well I guess if ultimately you're giving it to an append then like I guess it's fine all right so here there's this right so where were we we were making sure that we append this is and header it's saying we also need these things so we need to figure out what to set for these so SVG was it set on node assign with to something we don't know what that is yet does that return mute self no we don't know what these values are yet but we know that we'll have to set them sign load wait did that really just say load and not on load no let's set on load sign view box to be that and I guess these we can probably stick up here as well so where does width come from so I guess header takes image with and probably the same here okay so where does image width come from a sign good catch thanks so where does image width come from image width seems like something we need so we're gonna set up here 1200 and where does image height come from it's computed from font size and depth max and frame height and y pad I guess it sounds like there are a bunch of these like size parameters we're gonna need right we're gonna need probably these let's just like grab those for now there at least one more we need no I guess max depth these max so where does depth max gets set like it's like further down but oh interesting okay so these all get set to something and this gets up to something and this is this great alright so now we have a bunch of variables that we're probably gonna need later and here image width do I need to convert this to something or can I into value great so I can just say image width here I guess that is always set and that is always set these we're gonna have to set ourselves down here specifically it's gonna be set here and that's gonna be image width and image height and we don't know the image height yet and this is gonna be image height and so there was some code here for setting right image height is gonna be font size times 5 and then there's this whatever that does and then it prints the SVG and then it exits with an error hmm so as we probably do want to do here is like return error well yeah I'm not quite sure I think we want to do that from I guess probably something like invalid input invalid data maybe no stack counts and now we just need to figure out what this text writing is right so it's whatever this is and specifically we're probably gonna want some kind of wrapper like this for for strings though maybe not I feel like many of these are just gonna always be the same right so string TTF are they generally always font type font size that fourth argument seems to always be 0.0 angle okay and image width divided by 2 is that always image width divided by 2 no I see hmm so what we probably want here is like some kind of handy struct for writing texts so we're gonna have a struct write text what's gonna have I guess color which is gonna be a string what else size which is gonna be a string X is gonna be a probably gonna be a U size come to think of it why which is probably gonna be a U size text which is gonna be well a string location which is gonna be a string and extra what is extra what is that ever used for middle that is the extra part right no that's location middle middle oh it's like additional arguments I see sure and I guess that can be an option if we really want to and then we'll have string is gonna be it's gonna take a SVG was gonna be a mute SVG document and yeah I feel that way to Dutch ghost that like other languages are now scary because I can't I can't say whether things are mutable or immutable or what returns what like reading pearl code for example you sort of need to guess that types and return values and it's pretty pretty unfortunate okay so we want this closure to take the SVG and a description of the text and what it's going to do is basically what string TTF does right wait does string TTF ever get called without location set because it defaults location locations thing after the string middle middle blank blank okay so it does get it is blank sometimes fine so well we want here is SVG dot see normally you probably want it within a group right so here you never enter a group whereas here you're also not in a group but down here you are in a group I see so you may or may not be within a group and so I think what we want here is then just parent and that's just gonna be some SVG node and you don't know which is that exported through the great yeah I think that's what we want biggest scary stuff is copy or borrow yeah interface oh interface in Golang is yeah I've had some pain with that that's definitely true string TTF right so what it's going to do is it's gonna create a text which is going to be SVG nodes right nodes node text new no it's damn it it's not actually gonna be it's gonna be a node text it is not going to be a text node it's an element that's a text and I guess it's gonna be text new dot add SVG node element no node text new text dot text I guess this is gonna be item so I guess we'll make this text item right so we're this is just setting the content part right and then we'll want to have is it a sign was that what it was called on node it was a sign and append yeah so we're gonna do but on tech on here it's set that is such a weird interface okay so we're gonna set we're gonna set text anchor to be parent dot location dot unwrap or left we're also gonna set X to be X is in fact an f64 is that even true I I don't think I don't believe it here it takes the integer value right takes the integer value of what it is about to pass in as X same here same here oh X pad okay fine fine it'll be an f64 then X is gonna be format point two parent dot X but it doesn't do the same for why why why does it not do it for why why does it round X but it doesn't round Y if it says so font size it's gonna be parent size no these should none of these should be parent these should all be item and ultimately this is not to say text this can just be parent dot this no I think we do need to be text actually for extra so XY on family which we're going to set to family what's it called with font type so it's called and font type defaults to verdana so we're just gonna make it be that for now fill is gonna be color is gonna be item color also notice that angle is passed in but it's never actually written it's just unused it's like an argument you can pass in that is entirely ignored in the entire program that's good okay and then it's extra arguments so I guess extra I sort of want to be an iterator so I think what we'll do is might as well I guess it's gonna be an iterator item is gonna be string and actually I guess we don't really know what is it used for that doesn't have any extra that doesn't have any extra ID is details okay so it's a bunch of pairs and it's probably fine to require that they're all strings but are there any they're not key value pairs no great so then it it's probably gonna be just this and extra it's gonna be I and now that's gonna be texts and then for key value in item dot extra text dot assign key value and then we're gonna do parent dot append text why is it complaining about this wrong number of type arguments really I can't omit that and have it just infer it because that's what I want it's not gonna work is it that is not gonna work it's gonna have to be generic function I think it's a little awkward but fine no methods set for string because I'm missing these image height all right we haven't done this yet it's gonna be and append cannot be invoked on a trait object really are you for real requires a selfishized well shit I think this just means that this really has to be a generic function so it's gonna have to take a node and a iterator mute n and an item which is gonna be a text item I where n implements SVG node and I implements which also means that the struct that I made has to go here also means that this bound I can just steal and place here like so now I don't need that anymore now why is complaining expected one of those that's because there's an extra comma now it's complaining about expected zero found one oh it does still have to be generic over I undeclared lifetime it's fine great okay so that works so now in theory we should be able to write out strings yay in particular what's the string that we wanted to write out in the first place like how do we get into this business it was by trying to write out the error string just like up here somewhere yeah so we want a color yeah so the other thing we're gonna have to failure figure out his colors we might actually want to use a color crate for this but I think for now I'm just gonna hard code that so we're gonna do right SVG string we're gonna pass in mute SVG and text item where we're gonna have color is gonna be RGB 0 0 0 which is just black you're gonna pass in font size which is gonna be font size plus 2 because who needs things to be consistent and font size we've already declared up there great this is gonna be size is gonna be font size plus 2 image width divided by 2 is gonna be x and y is gonna be font size times 2 the text is gonna be this probably not PL location is gonna be some middle and extra is gonna be none I think that should work option is not an iterator really this option really not an iterator feel like that's false since when doesn't it I'm almost positive that that's not true oh it's into iterator fine fine we want into iterator expected string found integer us I think size can probably just be a u-size x and y except expected f64 in I guess actually let's do you f64 from that from u-size not implemented for f64 really oh fine image width huh I guess it'll be this as f64 it's basically complaining that I'm typecasting to a type that might not have room to fit all of you say you size as in not every u-size can be losslessly turned into an f64 which is true great so I guess this means that if I run it for the standard in and give it no input what does it do it says did not have a start time oh it crashes here no how to fix that basically the issue is it gets a although in niche yeah it never although it should still add this to temp I guess fine come back my prettys come back come back you beautiful creatures actually they don't need to be there let's see what it says oh I see it immediately goes to the last one why doesn't that crash the pearl script oh it doesn't assert that the thing it gets out as empty so I think the workaround here is probably to be oddly specific and say if key dot function is equal to nothing and key dot depth is zero then return zero didn't ooh that's it oh no this is even this is even sillier down here we should only call this flow if lot if not last is empty that's really what's going on yep and so now it correctly gives us some text output I guess let's stick that in fail dot SVG and let's open fail dot SVG hey gave us an SVG we did it we have an error SVG now let's see if we can actually make this work when things are non-empty it's really what we're after now that I want great this bit this currently prints nothing great so now we're past this sort of slightly boring bottom one I guess there's some time max stuff that we're gonna ignore this had a debug flag that's true I should do that arguably for all of this I should just add logging but I think that's not it's not sufficiently important to spend time on it it's something that we will add later like this code is going to be cleaned up regardless right because we're going to move things into state is going to be moved out to external to some external struct and stuff and once we do that then that's gonna help us clean up a lot of these things you sometimes do by way stepping through the program that's something like GDP or is it usually print debugging I very rarely go to GDP for these types of things it's because I don't think it's more efficient like it might be I don't think it's significantly faster I do it if something seems really weird and I cannot figure it out but usually print debugging is totally fine it's probably mostly because of habit more so than because that's like better in any way okay so they have time max time max this time is that what that argument was called so just dash dash time max why are they named different things and why is total not documented all right so back to that then so down here the next thing we do is time max is that and then we compute these do we have a min width is that a thing that we define yeah I cannot divide float by use eyes sure you can just try harder you did it oh actually I probably wanted this one to be with per time this to be an f64 is probably why one because with per time we're gonna multiply by by the time and so the fractionals actually matter when we do this division otherwise you get quantization of the of the widths prune blocks that are too narrow okay sure prune blocks that are too narrow so this is really just walking through and this is just retain this is just node dot retain and this is now a vector so we don't care about that this is really just each node is a I guess we could call it a location what are we gonna call each of the boxes the naming that would help a lot like what what do we call each one of these because they're not a stack a stack is the whole thing they could be an item I guess but sort of generic call it like a box but that's relatively uninteresting a frame I guess frame is good well it's not really a node because a node is a node in our SVG so I think this frame is pretty good for this actually frame is better for that but just say that that's a frame and notice we don't have to do the splitting anymore we don't have to do that because we already have the start time and so all we're gonna do is we're gonna retain it if oh and they also do depth max is gonna be node depth max is gonna be zero and then down here we're gonna say if frame dot end time minus frame dot start time is greater than men with time is keep actually no if if it's less than men with time then true all the way then false otherwise depth max is gonna be of depth max and frame dot item dot depth so we're just keeping keeping running track of the the highest the maximum depth because that's how we're gonna determine how tall the the plot has to be right so notice here the image is as tall as there are stacks can you zoom in a bit on the code on github yes I can indeed better node name already being claimed by SVG it's also not really a node right like I like the idea of frame because if you have this stack then each of those is a stack frame the where it gets weird is the question is function a depth of frame or is timed function a depth of frame and I think this is a frame time function at depth it's gonna be frame and then the question is can we come up with a better name than function at depth function at depth is really a what is that it's a okay the other way to do this is actually call call this a timed frame yeah let's do that this is a timed frame and this is a frame yep I like that and then this can still be frame that's fine it can I really not compare integers to have 64's which direction do I want this to go I guess maybe this way did I make rust no I'm gonna go with pretty far from it actually really do I I mean but look at what the compiler did there it was like this is now interpreted as a generic that's probably not what you want to use this it's kind of cool this should be standard compare max great it's also fun that this working is it producing empty SVG all right so now we have this bit draw canvas and embed interactive JavaScript program this is gonna be image height is gonna be depth max frame height y pad y pad and there's like some subtitle text we don't care about the header we've already dealt with that is the header and now there's this business which is like a bunch of script stuff and here we're gonna have to be a little bit careful so what it's doing here is pulling in a bunch of JavaScript code which is fine but notice that JavaScript code does have some some variables from the run in it I think the way we're gonna run or work around that is we're gonna have one I sort of want this to be in an external file right so actually let's just go ahead and do that straight away so we're gonna take this entire block of JavaScript to here and we're gonna edit source flame graph flame graph.js and then what we're gonna do up here is we're gonna use include str which lets you include a file directly into the binary right which is what we actually want here but we're gonna have to do that a little bit carefully because we need to wrap it in the appropriate tags I don't know what depths is what is it depths if you say so so I guess here we're gonna do svg.append and we're gonna append these things in order we're gonna append a svg.node node element I can never remember node element definitions great definitions new that has added a node no svg.node element linear gradient what is that called here it's called linear gradient I really wish this provided this svg crate provided nicer constructors that were based on what the field actually was there are other svg crates svg rendering library so what I want I mean I guess we'll just use svg then so linear gradient probably also just has a new and add fine okay so we're gonna have that new what else is in here so that on that we're gonna have to do dot set ID background just get this form at it correctly set y1 is 0 set y2 is 1 x1 is 0 and x2 is 0 and we're also gonna add a svg.node element element stop new dot set stop color bg color one whatever that ends up being and set offset to 5% and then we're also gonna add one that is 95% and that's gonna be bg color 2 that's our deaths we don't have a bg color one so we're gonna have to find that this is another thing we're like we're gonna end up with themes at some point and that I'm gonna leave for later because it's like not interesting to deal with right now great and that what what is company my now oh right that's fine flame graph.js things relative to the current file great see so now we've got our deaths so that's what we wanted and then we're gonna have a style so we're gonna have basically this entire same thing except we're gonna add a style and under the style let's see that's gonna set type text CSS add svg.node text new this business right and then comes the script that's gonna be a node element script where we set type to be text ECMA script and we're gonna have a text node which is gonna contain what is in that file right I'm missing a what is wrong takes one parameter ooh why does it take parameter why does this one take a parameter as opposed to all the others well I guess that takes the content we still probably need to set the type though how's that ooh style also takes its argument directly I mean that is kind of nice make it a little better it didn't wrap it in C data though which is a little disturbing because I thought the whole point was that it should do that is there not a C data data data attribute huh okay so we may have to place in the C data bit ourselves although I think you can just con cat the C data ourselves what do you mean see data in XML so that seemed to work about right what we still have to do though is figure out also I want this to be there oh apparently it does not okay so that means we now have the JavaScript we still have this issue of there are couple of things here that are basically anything that contains a literal dollar sign is a variable that is supposed to be taken from the external context I think what we're gonna do is we're actually gonna declare those in a separate JavaScript block that we dynamically generate and then we have those be variables that are available in here right so it's gonna be name type so I guess what we'll do here is this format and it's gonna be this and then in here there's gonna be a what's it called name type name type is gonna be equal to something we're not entirely sure yet oh man that's awful can I please have this be nicer none of these are really all that much nicer right so what else did we need there's name type so now this can just use name type instead what other dollar signs do we have that's not important font size and font width font size and font width those are gonna be globals that we don't care about x pad we need it's gonna be global x pad is fine x pad is fine x pad is fine x pad is fine inverted I guess var inverted is gonna be false search color birch color it's gonna be something all right great so those are the only variables so the remaining now is just like regular JavaScript code which you can now keep and edit in our own file which makes it nicer than having it directly in line like the pro one does we do need to figure out what name type is though name type is only used there okay so name type is gonna be function oh this that's fine uh font size font size is presumably just going to be font size and it's a number so it doesn't need to be quoted font width is the same x pad is the same and search color that has single quote strings as well that is true I suppose I mean does it really matter to us is this really better not convinced that that's better but sure all right so now we have all of that taken care of in theory like when this now generates a JavaScript it should generate two script blocks one that declares these variables and then one that later uses them and I guess actually this is going to need to have new line there and a new line there I also want this to not be indented oops whoa that's not what I wanted at all it's a little awkward but great this is a I sort of want to remove this from inline in drawing the svg but again we're going to tidy this up with functions later okay so we've now got all the way past the JavaScript so there's not that much left right there's only really printing the stack itself include ink okay so that's just adding that string and there's a filled rectangle so it looks like filled rectangle is the thing we're going to be using a bunch right if we look at filled rectangle filled rectangle filled rectangle um but I don't know that this function is worth extracting out because it doesn't really do anything all that interesting yeah um I guess we could keep it around sure let's just sort of remind ourselves that this is a thing that exists by doing this get rid of that um and filled rectangle here so now we're going to set up the background which is just going to be svg I guess append um node element rects or I guess there might be any even filled rectangle here oh just rectangle it's going to be a rectangle new and then uh it's going to have all this stuff on it and specifically in our case it's going to have uh set x to be zero set y to be zero set right those are the first two argument x and y just printed with point one which doesn't really matter to us uh then image width and image height uh which is going to be image width minus zero because of this business which is just going to be image width so that means that what this really is oh but that is width though hmm image width set height to be image height um set fill to be fill which is this great so that sets the entire background there's no extra great and then it defines a bunch of colors which we don't really care about so there's title text what is title text title text by default is nothing so title default it is so now we're gonna do we're gonna do our writing right which is gonna be uh directly onto the svg with a text item and certainly the text is going to be this um the color is going to be black which is going to be uh rgb zero zero zero um the size is going to be font size plus five this is going to be image width divided by two right that's the same thing we did up here for the for the error case it's probably going to be the same right yep so these are mostly the same um we don't have a subtitle text uh and then we write out whatever this business is but we have to write it but see even here i think that this rust code even though it's longer in terms of number of uh inverted is zero not false uh oh is this for the include you are right huh is zero here intended to be false or true zero is intended to be false so i think what i want this to be is this and then this this this used to say equal equal zero which is just the same as not inverted yes which is weird the negation of inverted i think that's right um what i was gonna say was um i think this is easier to read than this right even though this is shorter because it's one line i think this is like it's much clearer what's going on um here font size is going to be normal black x is going to be x pad y is going to be image height minus y pad two divided by two because of course uh it's going to be text is going to be space location is going to be some empty uh which is none an extra is going to be it or once of id and details right and probably these need to be as f64 that's i mean arguably these could just all be f64 so it would be fine okay next thing uh reset zoom all right so that's x pad this is font size times two which is also going to be as f64 uh text is going to be reset zoom and then it's going to be something like id unzoom on click unzoom the other thing that's nice here is we get um escaping just like for free which is pretty neat this is going to give me not what i want it's going to give me a reference to tuples in the iterator which is also pretty annoying so i i'm not actually allowed to do that i have to make this a vector even though man that's unfortunate it could use a bar or an asterisk in the traits but i don't think it can be bothered so fine we'll do that we could also chain itters so but we could do this without allocation but unclear that's worth it um and i guess this we don't need anymore for reference this we don't need anymore this we don't need anymore um so what's the next thing we're writing out font type font size x is going to be image width minus x pad minus 100 it's going to be y is going to be font size times two this is going to be search uh this is going to be empty it's going to have id search on on mouse over search on mouse out search out no search over search out on click is going to be the search prompt style is going to be this which is maybe the same no not quite no that's not what i meant to press i meant to go left right and there's one more one more before we get to actually printing the data um image x by zero this is the y is going to be the image height uh minus y pad two over two wait was this already yeah that was the same as that okay y pad two over two as f64 empty text empty location extra is going to be the id is matched oops and we're gonna ignore the palette i wonder what this now gives us like if i write this to uh what did we call this okay dot svg well we do get a flame graph and it does seem to have some kind of height i guess uh let's figure out what we're supposed to compare against flame graph of that to compare okay so the height seems to be correct and things seem to be in the right place so that's pretty promising uh the background doesn't seem quite right maybe yeah i don't think the background seems right which is a little interesting uh vg color no yeah so this one one oh we have a different that's why so why why did we get bg color one wrong bg color one oh i don't know how i missed that must have looked at some conditional somewhere so now great so now you see the only thing that's missing is the frames and everything else seems about right so that's a good place to be in and now we get to the business now we're gonna actually draw the frames yeah draw frames okay so uh for every frame in can we instead of having this be called uh node like instead of having this be called node can we just call it frames oh man searching for node here it's going to be frames dot push frames frames frames frames frames node node node node node great four frame and frames let's see what we got to do we don't need to split because we already have it separated we don't need to do this because we already have the thing uh end time is time max i don't think that's necessary don't think we're gonna have to do this oh i guess it's um if time max is greater than time then e time has to be uh increased that's fine we don't care about that and if not inverted this if not inverted business is really throwing my head for a spin here uh wait these are constants right oh depth they are not constants fine they're almost constant constants but i guess not uh so that means this can go away and we can say let here great it's gonna complain about uh right it's not s time it's frame dot start time and frame dot end time and frame dot depth and then down here uh this is frame dot i feel like instead of item that should be called location just seems more seems more correct great now we have the values um what is factor it seems scary factor is one so that's not that bad if in doubt set factor to one so that is just samples samples is frame dot end time minus frame dot start time why are we computing this over and over again we're computing it for retaining as well we might as well just compute this when we first construct it like memory is not going to be the main thing to deal with here add commas pearl per pearl fact five what i'm gonna guess that is this thing great well add commas to numbers don't understand why this is necessary like is it is this actually just adding a comma to the end of the string like why is it different plus or minus any number of digits followed by three digits oh it's a thousand separator it's adding thousand separator uh isn't that the default for if i do like if i do this for like what will that print uh isn't there a separator the separator crate because of course is a crate that seems fine actually i sort of don't want that it seems like a very unnecessary dependency because all we really needed for is u size also that is not an efficient way to do that okay but where's the macro macros that is very inefficient i'm glad we don't do that uh really if the toll night has one then it's probably good uh thousands do you mean this one because that's a great name for one of these digit group let's do no recent there are too many of these so i don't want to use separator because it's allocating two strings for every time it's doing it uh oh wow that's awful i don't want to do that at all um but there's this what does that do so we're slip allocated strings whoa except it also allocates a vector i guess feels silly to like depend on a crate for something like this but i guess maybe thousands just thousands do source separable so this first prints it to a string and then creates a new string and then walks that that seems unfortunate too is there nothing that does is directly oh human yeah that's good floating point to string conversion oh this is probably the total nine one you were thinking of but does it also work for integers because that's all i need it's a really fast way of printing things that's good but does it print integers format floats nope human was a good search though human time human size let's file sizes really time print messages for humans human size human readable strings but that's even fewer downloads well so the other thing to search for is ita fast functions for printing integer primitives to an io right great but does it allow me to do comma separated ones is the real question oh no this is just this just prints the number directly see that makes me sad so pretty toa does call into ita so i think we're just gonna go with this one and it's gonna be fine it's like not worth spending that much time on so we're going to use this and then we're going to do dot thousands and what was the where were we here because sort of what i want is the ability to just just print instead of allocate right like this is going to give me back a string whereas really all i want is to well i guess we do need a string to pass it to svg so maybe it's fine anyway my info so let info is going to be if funk no if frame dot location dot function is empty and frame dot location dot depth is zero then we want this right so this is where it's a little unfortunate that we have to create a string for samples text and then we have to create a second string down here that seems sort of unfortunate but i guess we'll survive what is count name oh it's for like internationalization or something probably yeah so we're not gonna have count name i'm just gonna say samples uh otherwise i'm gonna do whatever is down here which is oh i see a lot of code coming up that we're not gonna have to write which makes me happy i guess why are we writing time max up here okay fine well we'll keep this line just because we already have time max defined if um see down here it's escaping things for svg but we don't need to do that because we're using an svg library at least that's the idea i mean i assume that it will escape attributes correctly otherwise it's being weird uh this is an if not so that is indeed the thing we want so format that this samples was a dot two yeah and so we want to format frame dot location dot function and oh this does need samples text this needs sample text and also percent one thing that's weird here though oh i guess we do want that strip annotations we're gonna want to strip out annotations so um i think what we want is is de annotated is frame dot location dot function if let's um ai is de annotated dot is uh sure uh r find underscore square bracket if uh ai is well we sort of only want to look at the there's no reason to scan the entire function name but it's probably gonna be cheap enough anyways probably not gonna matter if it does matter we can fix it later um if de annotated ai dot dot is he um dot and one two three four and de annotated dot ends with i guess this is really if de annotated dot ends with see this is where i want uh chained while let patterns which are on their way but not there yet uh so if it ends with square bracket then we find the matching this if there is one if that is for long uh and de annotated uh ai plus two dot dot ai plus three so that is the character that is in between um is there like an is one of because that's sort of what i want here i want to check whether it is one of these characters um ha me then de annotated is equal to de annotated dot dot ai and this should say de annotated i wonder why de annotates it's like not entirely clear to me why uh why that's necessary if not to find delta delta is not defined so that should be correct um what is this shallow clone oh this is um so name atter if you remember is like also a um an option that users can give to like set additional fields to be set on all basically all svg attributes or function attributes file holding function attributes um whereas for us like every g is just going to have these fields that's all that really matters to us and so therefore what we're going to do next is create our g so we're going to say group is going to be uh actually it's just going to be svg.append svg node element group new dot set so we want class to be funk g uh we want on mouse over to be s this we want on mouse out to be c we want on click to be zoom this uh we want title to be info and it's not quite all we want but it's a good start because we also then want to pin stuff within that right so that's just the group start I guess we do need to check whether there's anything else in group start but I don't think so that's just turning the map into a string that is like key equals value if there's extra then add extra add g with the attributes then add a title element oh I see only these are added as attributes class on on mouse over on mouse out on click title is not there's also style which we're ignoring so then it's also add a title element that contains the title only if there's a title why would there not be a title there are other calls to group start that don't have a title I don't think that's possible should be the first element within the g container I mean that's fine um so sg node element title so I guess let's find out whether title is one of those special ones that always takes a value no it is not so title new add sg node text new info what else does it do if href oh I see so one of the things you can do if you have this like custom files you could set hrefs specifically you would say set a name at her for that particular function so there's like an href for it for example so that when you click it you go to say the source code of that thing but that's not something we currently care about uh pearl is really a right only language yeah I mean it's been said before and you're not wrong if the title is empty because erect is too small to fit a title oh I see what you mean yeah you might be right although no this hasn't checked length yet the length check happens down here and that just decides whether to write the string the title is there regardless the title I think is the um the hover text right so that title is always going to be shown even for the tiny ones it just for text that it decides not to show them so the question is why would that title ever be empty given that we always set it to be a string it's always a non-empty string so I think this is still right um okay so now color if function is equal to dash dash oh if you say so um I guess color is still something we have to figure out but I think what we're gonna do is just for now steal this color uh what color are you you are this color great everything's gonna be that color for now for now we'll we'll get there we'll get there um let color is equal to this um and then we're gonna do a filled rectangle whoo oh but that has to go within the group right this is where this gets awkward well I mean it's not really that awkward it's just we're gonna have to do some more things with the group before we uh before we append it so we're here we're gonna do group dot add group dot assign was it or is it append like these names man append assign is for arguments with mute self append is for children with mute self and we want node element rectangle new with um this business oh man so I did actually kind of want did actually kind of still want that function then just to be able to refer to it later as all this I'm here somewhere even further there so set x is going to be x1 do we have an x1 we do right yeah uh set y is going to be I assume y1 but now is where it gets a little strange because we want to set w and h right sorry width and height and um what it passes in is x2 and y2 right which is the end of the box and so this is really going to be width is going to be x2 minus x1 and this is going to be y2 minus y1 the question is is it really more efficient uh how maybe it is like wondering whether we should just compute the width directly instead of computing um instead of computing the y coordinates but maybe not actually because the um yeah I think so the width is really just uh samples times width per time and the y is just yeah hey why are we even doing this this so width is just samples is up there uh is really just samples times width per time actually that's uh just to sanity check ourselves right so that's what it was and this is what I claim it is similarly here uh y and height I think the height is just frame height is that true that's height frame height plus frame pad right if you subtract this from that sorry if you subtract uh what does it do what's the order in which it does it y2 minus y1 yeah so you just take this subtract that so this goes away that goes away okay this becomes frame height negative frame height right minus frame location depth of frame height times oh my brain is hurting I think this might be minus frame pad that makes no sense I guess here's the question uh why one is going to be the is going to be higher values is going to be further down in the plot and then it's going to be the height is going to be up by frame height yeah so I think it's just frame height I think I take that back so okay here's the here's what we'll do instead of trying to figure this out in my head I should say width and now this down here is just going to be why is it printing those in like point 1f does that matter I think it's doing that just to just to generate a smaller output file isn't there um first assert needs to be with oh yeah you're right reuse size what thousand set for use size I think that's false parentheses about value unnecessary that's great thanks rust compiler the annotated this it's not going to have a factor but this does have to be as f64 though oh is it doesn't really make much sense anything else I'm missing then yeah right so the width and height here that's what I was going to look at um f64 I think there's a round no that's not what I want I want round to a given number of decimal places but maybe there's no such thing that's kind of odd guess not let's just leave that for now I mean it's I think the only benefit of only printing out parts of that fraction is to reduce the final svg file size which like is totally a reasonable thing to do it just like doesn't matter for us right now it's going to be x2 minus x1 which is width divided by should these be f64 they are f64 font size times font width actually no these can totally be that it's already an integer why does it make a string ttf even if it's empty that I find a little bit odd look how close we are to the so if fitcars is greater than equal to three then oh what's this de annotation again I guess fine annotate just factoring that out because why not otherwise the annotated is f of this no is the annotate annotate and we don't need a function for it we can just there oh that's why I wasn't getting things that should make it out here four two six saying use size times float so what is called truncate truncate then I guess we do that again it's a little awkward but fine um let f is that f is f if f dot len is less than fitcars but len is the length in bytes whereas what I really want here is like the length in characters it's a little unclear what the right thing to do here is actually for like if you have a function named it contains like an emoji then it's fine first like it's going to be multiple bytes but it's going to fit you don't need the dot dot dot then f is going to be f dot rs dot take dot chain uh hitter ones dot dot it's not going to let me do that is it because cars gives me characters uh oh I see it only de-annotates if it doesn't think it can fit so uh no always uh don't show so this is don't show the function name and then we what we sort of want is if it can fit then don't truncate it no need to truncate otherwise uh need to truncate and in that case I think the trick here would be we could actually here just do um we could be really sneaky and just directly mutate the underlying string in theory but it would probably be graphemes yeah I what I do want I want basically an iterator over graphemes is that not what cars gives you oh maybe not scalar value yeah but I don't think there's a yeah I don't think there's a easy way to go over graphemes I think what we're going to do is just stick with length for now use unicode grapheme clusters instead yeah it's unfortunate but I like I don't think it's worthwhile to spend time on that right now probably but it's a it's a good like thing to fix later certainly the original percode is not doing this right either no need to truncate so I guess really what we're doing here is we're getting the text to write this is going to be cow from nothing this is going to be cow from f um and this is going to be string with capacity fig cars what does it do though it uh I see yeah so this substrings out that long and then replaces the last two with dot dot which is basically what we're going to do except we're going to do it this way we're going to say um s extend using f cars take minus two and then s dot push string this and now we should be able to do our right svg string uh using uh this is now going to be the group and we're going to do a text item and that text item is going to have color it's going to be it's going to be color uh what else uh size so size is going to be font size right I assume yep uh x is going to be x1 plus three so I guess three here is the padding y is going to be three plus y1 plus y2 the average of y1 and y2 that's funky oh it's just centering okay sure uh so it's really this is just x this is y plus height divided by two text is text which is going to be this location is none and there's no extra and then what does group end do nothing in particular great 0.0 0.0 cannot add float to use ice huh I wonder whether you should just keep it as an f64 instead of doing this conversion right like just do this well sort of I think it's fine for that to be used size the trick is just here I guess this is going to be ass f64 it's going to be ass f64 and that's going to be ass f64 what is it complaining about oh uh oro cow how about now ooh crashed okay so our computer's wrong uh so minus frame pad what is frame pad frame pad is one great 387 is samples oh it's the the rounding that's getting at me we'll just stick with what the original code does and then we can just cry ourselves to sleep instead so this is going to be y sorry this is going to be x2 minus x1 this means y2 minus y1 this is going to be uh x2 minus x1 this is going to be y2 plus y1 plus y2 as f64 divided by 2.0 and this is now x1 right how about now we got a file what do we think this is going to look like okay so remember this is the target whoo that was not what we were after so a bunch of this seems right that seems correct so these g's huh so a lot of new lines that this library is injecting that makes me a little sad uh x is 10 y 7 41 the text is empty for a bunch of these oh it is not escaping text contents it is definitely not escaping text contents really like actually though why does it not escape text contents like what kind of library is this that's not ideal parse is this better what this is this just seems different i don't think that's what i want documentation no scripts external resources and complex css styling okay that's fine i don't really care about the parsing we'll not be preserved that's fine the real question is can i generate the svg that i want enums i want usage examples examples resave this is all parsing i want to generate an svg like how do i make a thing oh it's fully typed cause me some issues too hmm that doesn't seem very helpful fine xml escape i mean i guess i could just write this out as xml directly it's not like the svg thing is giving us all that much right so that is kind of neat we could totally do that it's a little inconvenient to write um the svg out this way where you like basically have to write a stream um but we totally could yeah okay sure i guess we're gonna switch this over to doing uh xml instead of this funky svg thing all right so quick xml instead zero thirteen and we're gonna use this and probably also um event all right so anywhere we're currently doing svg stuff we should not do svg stuff great so show me how to write uh writer we're gonna do writer new and then i guess we're gonna have to do a it does mean we have to deal with opening and closing things ourselves and we're also gonna have to set up the svg prelude um which is perhaps slightly annoying but not that bad specifically i don't want this business so writer give me the docs um so writer is really just write event and i guess everything that i do on it is gonna give an io results no a quick xml results let's uh ignore that for now is it just write event that i'm gonna need probably yeah actually here i could probably just write bytes but i think i'm gonna uh yeah i think specifically this um in fact i don't know whether this xml generator is gonna try to emit that for me to try to be helpful what kind of events are there oh there's a doc type event okay it's fine i don't think i particularly care about that um writer dot write event so if the first thing we want is an event uh i guess start event start and that contains a bytes start i think we're gonna want some of these uh bytes start with optional attributes it's gonna be content from the given name oh i like this this is a good api all right so um it's gonna be uh borrowed name svg and then attributes it's gonna be it take i guess with attributes and with attributes things can be turned into attribute that seems fine let's make this a vec for now of all of these things substitute anything that is not an equal sign followed by an equal sign um we'll end this followed by an equal sign followed by double quote followed by anything that is not a double quote multiple of that to a double quote to uh why does width have a space around it oh i see all right so that's the event start we want um is there also one for comment great we're gonna want a bytes text as well so writer write event event comment bytes text and what is invite text please tell me uh i guess from plain oh it does not like this what is it complaining about now no rules expected that token that's probably true uh although width should be image width and view box should be oh i see we don't actually have all the things we need to omit this yet this is going to be writing out the header we can't actually write out the header yet because we don't have the height right like this is going to be image width and image height and this is going to be image height which we can't do yet sadly so this code is going to have to go further down although i guess that is pretty reasonable like there's no reason for us to really start writing out the svg quite yet so we'll do that down here um i guess we may want to have the header writing be just a closure up here a right header be something that takes a svg and image width and image height and then does i guess svg do that on all of them because now then this can just do right header write out the svg with the image image width and the image height should be all that's needed and similarly down here we should be able to do the same thing this where does this get a little bit tricky though is we also need to terminate the writer because i'm well i'm not sure whether does that automatically actually if i send a right to it that's a good question if i send a right to this thing um where is writer if a writer gets an unterminated event what does it do my guess is i have to terminate manually it's probably not keeping state um although that is pretty straightforward it's really just uh svg dot write event uh i guess writer no event end which also means i need a bytes end svg so that's not too bad and i guess is there uh i guess it probably just is it enough for me to just drop it is that all i need to do like how do i close the writer because it doesn't implement right i think i just drop it so that i guess is fine um so it doesn't mean we will have to remember to do that down here instead of doing that we're going to do this and that's fine uh luckily we now have this right svg string which is now a little bit different because it doesn't need to take this end anymore now the parent is just going to be there in fact is no parent it's just the writer which is a writer right i guess in fact it's a writer over a w where w is a right and in theory all that should have to do maybe write an end of file um well end of file isn't an explicit thing that you can write oh to the stream was there an event called end of file oh yeah you're right no that's totally what we should do totally right and i guess that should be up here too uh up in this business um the other thing i notice is that this also has declarations which we should probably use instead of like raw writing these which would then be svg write event but i don't think i care about that i think i'm fine without being unstructured um right uh sorry so we were down here um and here i guess what we want to do is output a text element so we're gonna do uh svg dot write event uh event start byte start borrowed name text uh and then we're gonna include the attributes from item dot extra right i think that's what we want and then in fact we can we can be as generic as uh byte start let's us be which is specifically that with attributes is anything like this and this of course should be quick xml quick xml at events attributes that and this will return a quick xml results of nothing mmm it's actually be going to be that and then with attributes again because we want to we also want to write out our own attributes here is there a way for me to not have to do that with an iterator like can i give it just one attribute and so something is a new byte start oh yeah push attribute great so we want this and then we want we'll eventually push that out um but then we'll do text push attribute uh and i guess for each of these we will push an attribute so it's going to be text push attribute uh so what implements oh great so attribute from this why is it complaining about these i just slice found stir specifically has to be bytes i can't use stars really i mean i guess that's fine oh right uh fine let's also include events attributes attribute from stir use size is not implemented see this is where it gets annoying it needs everything to be bytes although i guess it's gonna need to do that regardless but i really don't want to have to allocate for these but i may not have much of a choice so uh can't use b text oh you're right that's right item dot location unwrap it's complaining about this one this i think it will complain about the lifetimes of uh right and then once we've pushed out that start we're also going to have to write event event end bytes and borrowed b text and in the middle there there has to be a text thing which is going to be event text which is just going to be bytes text uh from plain stir so there i have stir okay fine if you say so uh what's the types of info that creates to be more flexible are you talking about the current one the quick xml one um i think what i want is for it to uh take anything that can't that is display for example um like ideally attribute would allow me to create an attribute from something that is display just for convenience like it still is ends up being forced to allocate which is a little sad actually i can probably do a little bit better here by saying by manually constructing the attribute uh by doing this uh key this uh value like this dot what's the thing to turn a string into a vec expand everything really isn't there one to just get the underlying vector it's like pretty sure that was the case but no much the case please um oh great okay yeah so i could just do here um i can do this instead and do uh vec from that which i think is the same as saying i can do this into right so i can do it it's just like i wish i didn't have to type this um at the same time it could be that this is like such an unusual case somehow um this is uh font size value is going to be item size dot two string like fundamentally if you have something that's a number you have to turn it into the character bytes so i understand that there needs to be some conversion process right wait why is it complaining about this into oh fine because it has to be turned into a cow item dot oh that's nice great so now right string should work and generally we were using that right i guess now this is a bunch of basically static so here we don't have to be as good about using the interface we could just print this out as raw text where it gets annoying is including variables and such so i guess write event event start bytes start borrowed name actually here i could do borrowed why does it name len huh i wonder why is name len necessary as an attribute like if you do um if you do this i wonder okay well because here what i can do is just uh give the for linear gradient instead of setting all those separately so i guess first i have to give a definitions what was that just depths i think it's depths uh so just one of these and we're also going to have a end bytes end of that and this has to be b because we have to give bytes but then we start a linear gradient gradient and then we have to end one but this we can just do borrowed and then give the values directly in here right oh can i do that is that a thing that works that's neat um because then i can do oops uh background uh y1 is 0 y2 is 1 x1 is 0 and x2 is 0 and then i guess i have to give it the length of that's that's going to be linear gradient dot len right so that saves me all of this um and now i need to write out uh now i need to write out borrowed name be stop with attributes um i guess iter ones um stop color bg color one chain iter ones uh offset 5 percent so this is a much more sort of mechanical construction of uh oops ah what am i missing right uh so this is actually an empty right because stop has no text content and then we have another stop that is bg color two that comes in at 95 percent and so this is really the same code should be roughly at least the other thing we'll have to do here is handle file has to be a oops handle file has to be a quick xml result otherwise it's gonna keep yelling at me um right so that's stop color right yeah now we need some css out there is there a way for me to just like give the contents as well in the event probably not no that's too bad um so in that case here we're just gonna write um it does have arguments doesn't it i guess fine so we're gonna write out a start it's gonna be a style with attributes uh type text css style um we're also later gonna have to do and style um and then in between these is gonna be a event text bytes text from plain string this thing so the whole idea here is this from plain string is implying that it's not escaped which was the whole reason we switched to this library in the first place um and then i guess we want the same for script and i guess we want text ecma scripts uh we're gonna have to end scripts see what bothers me about this is here it's suddenly a lot easier to like accidentally screw up right to accidentally forget to close a particular thing uh it doesn't mean we can write out all the script in one tag though so that's kind of nice um so we're gonna write out uh cdata write event cdata and that's gonna be bytes text from plain string format that please like so and then we're gonna do the exact same to write out the rest of the cdata so now we don't need to do this like cdata business ourselves we can just do this so that's kind of nice now what are we missing bytes and and now we need a rectangle i guess now well we might want to do i guess we don't draw that many rectangles do we it's not worth having a function for it um but writing out this rectangle is mostly static but not entirely okay fine so it's gonna be somewhat similar to this but it is empty though so that helps us a little bit um it's gonna be borrowed name rect with attributes uh x which is gonna be zero with and height y which is gonna be zero and fill which is gonna be url background so you see that actually ends up being about the same length when you format them the same that's not so bad it is a little sad that we have to use a vector for this though uh borrowed name has to be a bytes this has to be a this has to be a stir did the voice change i don't know did the voice change as in since earlier in today's stream i don't think so at least i don't think anything has changed on my side but in some sense uh you hear me differently than i hear me something is different um i don't think i changed anything does anyone else think the voice is different or is my voice the same it's gonna be x one it's gonna be y one didn't notice anything oh maybe yeah i mean the mic is pretty sensitive to exactly where i'm sitting um so that could totally be it yeah see this is what's making me sad now we have to do all of these like extra allocations for printing stuff out which i wish wasn't the case um probably not that bad but but still like this veck for example makes me sad um convert from string all right this has to be one of these and then i guess here we're gonna start a group this is gonna be start it's gonna be a g with just static arguments so that's nice but see i kind of want this to be this right there's no reason for that to really be a veck um but i think it's a little hard to get away from like so like i don't think that will compile something seems to have slightly changed since a couple of hours back but not in a sense that it's gotten worse i mean it could also be that my voice is just more strained because i've talked for longer um because we need a title node start borrowed name title title doesn't have any attributes um and then we're gonna have to end title here borrowed and in between we're gonna have a text uh just gonna be a bytes text from plain um from plain s