 Hello and welcome back to program analysis. This is still the lecture on slicing and We're on video number four where we will look at Dynamic slicing so a technique to produce a slice of a program based on an execution of this program So as for static slicing there is of course more than one way how you can do dynamic slicing The one that we focus on here is based on a paper from 1990 Which defines dynamic slicing as the problem of finding those statements that are Triggered during an execution that must be executed in order to give a variable a particular value so that means we Look at a particular execution, which means we look at an execution driven by one particular value And of course it also means that the slice for this value may be different from the slice that you would get if you executed the program with a different value So every execution with different values may also give different slices And why is this useful? Well, it's useful for example for debugging where you may want to Get a reduced program that still leads to the unexpected value that you're observing So you have an input you know that it triggers some behavior that you did not expect And now you want to know which statements in this program are really Relevant for this unexpected value and in order to do this you would essentially want to compute the dynamic slice We will here look at two different approaches to do this They are very similar, but they differ in one important Aspect which we'll see we start with the simpler of the two which I just called the simple approach What you're given for both of them is a so-called execution history Which is basically the sequence of statements or the sequence of nodes in a program dependency graph that are executed with a given input and now in order to compute the slice for a particular slicing criterion specifically for a statement n and some variable v is Basically two steps first you keep only those parts of the program dependency graph that is also in the execution history So only those nodes that are in the history Are kept and then the second step is basically the same as for the static slicing approaches that we've seen earlier Where we reduce the problem again to graph reach ability and in this reduced program dependency graph starting from the statement and in the variable v Compute the slice by looking at which nodes can reach our statement n as an example to illustrate this idea of Dynamic slicing we'll use the following so here Have some variable x that gets a value depending on some input that is read for example from a user. So this is the part that determines which Behavior will see and which we statically do not really know ahead of time and then we have a couple of conditions that check for example whether x is smaller than zero or maybe larger than zero or maybe equal to zero and depending on which of these Conditions is true different values are assigned to other variables y and z and then eventually these two variables y and that are written to the console So let's compute the dynamic slice for this example and of course because it's dynamic slicing we need to Look at a particular input and at a particular execution that is triggered by this input. So for the example Let's at first assume that our input is Minus one and then using this input the question is what is the execution history? So which of the many statements that we have here in this piece of code? Actually executed So if the input is minus one then of course the first statement so one is executed Then we will have this check at line two this check will be true because x is actually smaller than zero and that means that also three and Four are executed afterwards, but then five six seven eight and nine are all not executed because we have already taken the if branch but eventually 11 and Ten will also be executed So now to compute the dynamic slice based on this execution history We need to compute the program dependency graph again, and this is exactly the same as For the static slicing approaches that we've already seen So this is also a nice exercise if you want to just see how this program dependency graph will look like I'll quickly do this Also now for you here. So in the graph we have nodes for each of the Edges, sorry, we have nodes for each of the statements in the program so this and this and also 11 and Then we have edges for the two kinds of relationships that matter to us here one is the data dependencies Which I'll denote with this and the control flow dependencies With that color. So let's have a look at these dependencies and let's start with the data dependencies So at line one we are defining X which is then used almost everywhere else for example at two and at Three and also at four and Also at five and also at six and seven and eight and nine and That's it for for X Then we have this definition of Y at line three, which Will be used at ten. So we have a dependency from three to ten and similarly the definition of Z at line four Will be used or could at least be used at Statement 11 and Then for the other definitions at six and seven It's more or less the same. So what is written at six? Maybe used at ten and what is written at seven may be used at eleven and Then for eight and nine very similarly. So what is used what is written at eight? Maybe used at ten and what is written at nine may be used at eleven So these are all the data dependencies. Now, let's have a look at the control flow dependency. So basically Here the question is which statement or conditional determines whether another one is executed So the check at line or statement two determines whether three and four and Five are executed. So we have control for dependencies to express that and Then the check at line five will determine whether six and seven or eight and nine are executed, which means we have data dependencies for this and also like that and These are all the data Sorry control for dependencies that we have here and this means this is also the entire program dependency graph Now given this execution history and the statically computed program dependency graph the Yeah Idea to compute a slice is to first mark all the notes that are actually executed In the history among the notes in this graph and this means we will mark note one and note two and note three also note four and Then notes ten and eleven So this just means that these notes are executed And then the second step is to compute the slice we're interested in so let's say we are interested in this slice of Statement ten and variable y So basically telling us why this variable has the value that is going to be printed at this statement Then what we are going to do now is we start at Statement ten and then follow the data dependency and control flow dependency edges backwards in order to figure out which other statements should be included So going backward from ten, of course, we need to include ten itself Going backward we will find three and then further going backward from three. We will also include two and One but we for example to not include four and eleven even thought they have been Executed in this history. So the slice that we would get here consists of one two three and ten So to show some of the limitations of this simple approach and also to double check if you've understood these ideas that I've explained so far I have a second example, which I'll invite you to actually do by yourself So this is a little quiz for you and then afterwards. I'll show you the solution So in this second example, we again read some value from from the input and stored in n and then we have these three variables that y and i that are modified down here in this Loop and then eventually at statement nine. We are printing the value of that And now the question is if this last statement and the variable set is our slicing criterion And if read input returns the value one, then what will be the dynamic slice? Computed by the approach that I've explained so far So I'll invite you to stop the video here and just try it out by yourselves and the next I'll show the solution So let's have a look at how to compute the dynamic slice using the simple approach explained so far So I had said that the input we are considering here is one So this is what is returned by read input and as a result We can figure out which statements are actually executed and this will be the following So we first execute statement one then two then three then four the first time we reach this conditional of the loop One is smaller or equal than one. So we evaluate this to true And that means we will execute six seven and eight Now we've updated i to now we two and now we're checking that the i equal two is smaller or equal to n which is one So this evaluates to false. So we do execute five, but then do not go into six seven and eight again but instead Go to statement nine and this is also the end of this execution history So now to compute the dynamic slice we again need to program dependency graph and I'll just Beat this up a little bit. So we have these notes one for each statement Next we have this set of data dependency Edges which I'm not explaining in detail because this is a quiz that you should do yourself if there any questions feel free to ask them in Ilias and Then finally we have these three control dependency edges Which basically tell us that six seven and eight are executed depending on a decision made at statement five So next using the approach that I've explained so far we again determine which of these notes are actually in the history in the execution history so which of these statements are executed and It turns out that These are basically all the statements one two three four Then five then because we've gone into the loop once there's six seven and eight and then eventually there's also statement nine and now the second step is to Compute the slice and we said we interested in the slice Starting at statement nine where we want to focus on this variable Z So we basically start here and then follow all edges backwards, which means we also need to include this one and Also this one and because we have six we also need Five and also three. Oh now I'm realizing. I'm actually missing a data dependency edge namely from seven to six Which needs to be here because the value y that is Computed in one iteration of the loop here may be used in a different iteration of the loop there So this was missing on the previous Yeah, previously but because of this edge we now also need to include this statement seven and because We already have five we also need to include four because four can reach five and for the same reason we also include one and We also include eight also because eight is a note that can reach note five and now what you see here is that basically the slice contains all statements of this program and Well, this is what we get based on a simple approach that I've explained so far But this is actually not the best possible slice we could get here So for example, if you look at this execution history, then the statement Seven which computes a new value for y is not really relevant for the value that z will have at the end Simply because after having computed this value y we never use this value y again Because we just don't go again into the loop But the way we've defined this dynamic slicing approach so far it will include Statement seven and also statement eight even though they are actually not really required to compute the right value of z at statement nine So more generally what are the limitations of the simple approach that I've explained so far So that the basic problem here is that multiple occurrences of a single statement Are represented as a single note in the program dependency graph But at runtime, of course this single statement may be executed multiple times and different occurrences of the statement May actually have different dependencies But because we map all of this into a single note in the static program dependency graph These different occurrences of statements all get conflated into a single note and as a result the slides slices that we Get maybe larger than necessary as we've just seen with the example So now to address this limitation Let's have a look at the revised version of this dynamic slicing technique that we've seen so far Which is not the simple version anymore, but a better one that addresses this problem of having too many Occurrences of a statement conflated into a single note So in this revised approach, we are computing the slides based on a dynamic dependency graph Which is different from the static dependency graph in the sense that the notes here now represent occurrences of notes that you would have in the static Dependency graph and the edges do not represent the static data and control for dependencies as we've seen so far But they represent the dynamic data and control for dependencies that we've actually seen in a particular Execution and now given this dynamic dependency graph become can compute the slice again in a very similar way to what we've seen So far by again basically reducing it to the problem of graph Reachability so if you want to compute a slice for some statement n and some set of variables v that are used or defined At n then what we do now is we compute all the notes that are in This set as dyn that can reach any of the notes that represent occurrences of our statement n And then the actual slice Consists of all the statements that have at least one note in this set as dyn and as we'll see this is a Smaller set or this can be a smaller set than what we get with the simple dynamic slicing approach So here's our example again And what we'll now do is to compute this dynamic dependency graph where we have one note for every occurrence of an executed statement So given the history that you see here Which is the same as before we would then have notes for Statements one and two and three and four Then we have a note for the first occurrence and I'm putting this little One here to denote this first occurrence of our statement five Which is then followed by an execution of six an execution of seven and an execution of eight And then we'll have a second occurrence of Statement five here and eventually this One execution of statement nine Now given this set of notes we can very similarly to what we've done for the static Program dependency graph now compute the dynamic variant of it by again looking at data and control Dependencies so let's start with the data dependencies here. So the value and that is defined here is used in the first execution of Statement five and then also in the second execution of statement five because n just remains the same all the time next let's have a look at the Definition of that here at statement two. So this one will be used down here at Statement six. So we'll have an edge like this Then we can have a look at this definition of y at statement three, which will be used here and also here So we have edges from three to six and from three to seven Then this definition of I here at line four will be used here. So we have one from four to the first occurrence of statement five and This definition of I is also used here. So we have another edge from four to eight Then let's remain at this statement eight, which is also assigning again to this variable I which is then used in the second Occurrence of our statement five. So we basically have an edge from here to here and then finally The value is that that is used down here comes from this definition at line six So we have another edge here from six to nine Now these are all the data dependency edges. Let's now also have a look at a control flow dependency edges and Those are very similar to what we've seen in the static graph. Just that now we do this for the right occurrence of node of statement five and this is the first occurrence which determines that six is executed and that seven is executed and that eight is executed and That should be it unless I've forgotten anything These should be all the data dependency edges and control flow dependency edges in this dynamic variant of the program dependency graph And now given this graph we can compute a slice again, which we now do by basically starting At the node we interested in which is node nine and This variable is that so we start at the corresponding node and then go backwards in the graph that we now have So we will also include six. We will include five one so this first occurrence of statement one and then because of all the data dependency edges we also include one two three and Four Now what you see is that we do not include seven and not eight and also not a second occurrence of five and now based on these nodes that are included in our slice We can map this back to statements by basically including all statements that occur at least once among the Marked that yeah the the nodes that I've marked with red which means that we will include This one we will include that one. We will include this one and this one and this one and this one and Also this one But we do not include those statements here, right? So they are not part of this slice and that's the big difference to The slice that we have seen before with the simple approach because now we know that This right to why and this right to I at line eight Are not relevant for computing the value of that that we'll see at line nine at least not in this particular execution So as you've seen this revised approach of the dynamic slicing technique gives a smaller slice Which is probably better if you want to understand the behavior of the program triggered by one particular input where one particular Sequence of statements is executed one interesting property of this Variant of the approach is that it may actually produce a program that if you execute it with another input does not give the same value as if you would Execute this original program with the same input, but this is okay Because this is not the promise that this dynamic slicing technique is doing and instead what it's doing is it tries to give you a small Set of statements that is relevant for a particular value that you get somewhere And this is really helpful if you want to isolate these statements For example, if you're in the process of debugging or understanding why at a particular point in the program if you execute a program You're getting a particular value Just let me finish this by saying that there are many many other slicing approaches There is a very nice survey from Frank tip that reviews many of them And what we've seen here in this lecture is of course just a small subset of all the possible ways how people have defined slicing Alright, so this is the end of video number four which also terminates this lecture on slicing So you've now hopefully learned what slicing is you've seen two different static approaches to compute slicing and you've seen This dynamic slicing approach with its two variants here in this video and hopefully have an idea now How to compute a small subset of a program that is relevant for getting a particular value and a particular Program location. Thank you very much for listening and see you next time