Introduction

This lesson provides an introduction to the interactive debugger in R Studio that can be used to step through R Scripts (but not R Notebooks). It has many of the features of a typical debugger such as setting breakpoints and stepping through code and into and out of functions.

Before We Start

The reader of this lesson might find the short video tutorial below helpful in getting an overview of the debugger in the R Studio IDE for R. Consider watching them before proceeding with the lesson. Also, be sure to follow along in R Studio.

The video below provides a more in-depth debugging example in which functions are debugged.

Debugging

The process of debugging – or finding logic errors in programs – is a complex subject and often more art than science. You will find that you become better over time. This lesson focuses on the interactive debugger and not as much on general debugging principles. See the references for more information on that topic.

Debugging means experimentation, hypothesis testing, elimination, trial-and-error, and lots of patience.

Finding your bug is a process of confirming the many things that you believe are true — until you find one which is not true.

—Norm Matloff

From an abstract perspective, debugging means finding where the code does not work as expected. It runs but it does not produce the result you expect. This can be from an error in your programming logic, a misinterpretation of a language construct, or a defect in a package function or even the language itself. Naturally, the former two are most common and we will focus on those in this lesson.

To find why your code isn’t behaving as you expect, you generally follow the approach below within a debugger – although a debugger isn’t strictly necessary but very convenient. Once you are used to an interactive debugger, not having one will definitely be felt.

  1. Run your code
  2. Stop the code at the line where you suspect an issue
  3. Inspect the code for mistakes, inspect the contents of objects, look at the value of variables
  4. proceed running the code statement by statement and repeat step (3) after each step

It is often simpler to extract code that isn’t running into a separate “sandbox” – a new R Script that just contains the flawed code. That way you can experiment without messing up your actual production code.

Setting Breakpoints

To enter the debugger you need to enter debug mode. A common way is to set a breakpoint at a line and then run your code. It will automatically stop at the breakpoint. To set a breakpoint at a line in RStudio1, click to the left of the line number in the editor, or pressing Shift+F9 when your cursor is on the desired line. If that all fails, then go to the line where you want your code to stop and select menu item Debug/Toggle Breakpoint. A red dot will appear next to the line where you have a breakpoint. To remove the breakpoint, just do the same steps again – it is a toggle.

Now, if you run the code it will stop at that line and then you can go to the Console and print out variable values and run ad hoc statements to experiment. To run your R code like a program (starting at the first statement), you need to “source” the file. In R Studio that means you select the “Source” button.

Once you are in debug mode and you have stopped at your first breakpoint, you can step through the code line by line. Now, you need to apply your detective skills and your experience – and not just experience programming in R, but also all of your programming experience with other languages. Many of the same issues occur in all languages. For example, if you get an error when trying to load a file (like a CSV with read.csv) then you should always suspect that the file cannot be found. If you didn’t specify an absolute and full path to the file, then you need to assume that R uses a different default folder than where you believe you are. Once you enter the debugger, call getwd() to find out where R is looking for file. The short video below illustrates this common problem in R2.

To exit debug mode, you select Debug/Stop Debugging, press SHIFT-F8, or hit the Stop button within the debugging controls as shown below. Stopping means to quit the current program and stop any further statements from executing.

Of course, you can also use the controls to continue to the next breakpoint, press “Continue”, or step into a block, loop, or function, or execute the remainder of a loop or function. Experiment with each control to see what it does.

Rather than setting breakpoints in the editor, you can also include a call to the function browser() in your code which, when encountered, will pause execution and place you in the debugger – it functions as a programmatically set breakpoint and can be invoked conditionally.

The code below illustrates this. On line there is a call on line 6 to the function browser().

df < read.csv(file = fn)

n <- nrow(df)

if (n < 1)
  browser()

for (i in 1:n)
{
  # ...
}

Summary

Debugging, the process of removing logic errors, is an important part of programming. An interactive debugger, coupled with language-independent forensic strategies, is a valuable tool in the arsenal of the professional programmer. This lessons provided an overview of the key features of the interactive debugger built into the R Studio IDE.


Files & Resources

All Files for Lesson 6.195

Errata

None collected yet. Let us know.


  1. Setting breakpoints and using the debugger is only possible with R Scripts and cannot be used with R Markdown documents. To debug R Markdown code chunks you would need to copy them to an R Script, debug them, and then copy back to the code chunks.↩︎

  2. There is even a package to help with finding out where you are, the here package. It can help make code “location independent”.↩︎

LS0tDQp0aXRsZTogIlVzaW5nIHRoZSBEZWJ1Z2dlciBpbiBSIFN0dWRpbyINCnBhcmFtczoNCiAgY2F0ZWdvcnk6IDYNCiAgbnVtYmVyOiAxOTUNCiAgdGltZTogNDUNCiAgbGV2ZWw6IGJlZ2lubmVyDQogIHRhZ3M6ICJyLGRlYnVnZ2luZyxyIHNjcmlwdCxwcm9ncmFtbWluZyINCiAgZGVzY3JpcHRpb246ICJUaGlzIGxlc3NvbiBwcm92aWRlcyBhbiBpbnRyb2R1Y3Rpb24gdG8gdGhlIGRlYnVnZ2VyDQogICAgICAgICAgICAgICAgaW4gUiBTdHVkaW8gYW5kIGhvdyB0byB1c2UgaXQgdG8gZmluZCBsb2dpYyBlcnJvcnMgaW4gDQogICAgICAgICAgICAgICAgUiBwcm9ncmFtcy4iDQpkYXRlOiAiPHNtYWxsPmByIFN5cy5EYXRlKClgPC9zbWFsbD4iDQphdXRob3I6ICI8c21hbGw+TWFydGluIFNjaGVkbGJhdWVyPC9zbWFsbD4iDQplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSINCmFmZmlsaXRhdGlvbjogIk5vcnRoZWFzdGVybiBVbml2ZXJzaXR5Ig0Kb3V0cHV0OiANCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiBzcGFjZWxhYg0KICAgIGhpZ2hsaWdodDogdGFuZ28NCi0tLQ0KDQotLS0NCnRpdGxlOiAiPHNtYWxsPmByIHBhcmFtcyRjYXRlZ29yeWAuYHIgcGFyYW1zJG51bWJlcmA8L3NtYWxsPjxici8+PHNwYW4gc3R5bGU9J2NvbG9yOiAjMkU0MDUzOyBmb250LXNpemU6IDAuOWVtJz5gciBybWFya2Rvd246Om1ldGFkYXRhJHRpdGxlYDwvc3Bhbj4iDQotLS0NCg0KYGBge3IgY29kZT14ZnVuOjpyZWFkX3V0ZjgocGFzdGUwKGhlcmU6OmhlcmUoKSwnL1IvX2luc2VydDJEQi5SJykpLCBpbmNsdWRlID0gRkFMU0V9DQpgYGANCg0KIyMgSW50cm9kdWN0aW9uDQoNClRoaXMgbGVzc29uIHByb3ZpZGVzIGFuIGludHJvZHVjdGlvbiB0byB0aGUgaW50ZXJhY3RpdmUgZGVidWdnZXIgaW4gUiBTdHVkaW8gdGhhdCBjYW4gYmUgdXNlZCB0byBzdGVwIHRocm91Z2ggUiBTY3JpcHRzIChidXQgbm90IFIgTm90ZWJvb2tzKS4gSXQgaGFzIG1hbnkgb2YgdGhlIGZlYXR1cmVzIG9mIGEgdHlwaWNhbCBkZWJ1Z2dlciBzdWNoIGFzIHNldHRpbmcgYnJlYWtwb2ludHMgYW5kIHN0ZXBwaW5nIHRocm91Z2ggY29kZSBhbmQgaW50byBhbmQgb3V0IG9mIGZ1bmN0aW9ucy4NCg0KIyMgQmVmb3JlIFdlIFN0YXJ0DQoNClRoZSByZWFkZXIgb2YgdGhpcyBsZXNzb24gbWlnaHQgZmluZCB0aGUgc2hvcnQgdmlkZW8gdHV0b3JpYWwgYmVsb3cgaGVscGZ1bCBpbiBnZXR0aW5nIGFuIG92ZXJ2aWV3IG9mIHRoZSBkZWJ1Z2dlciBpbiB0aGUgUiBTdHVkaW8gSURFIGZvciBSLiBDb25zaWRlciB3YXRjaGluZyB0aGVtIGJlZm9yZSBwcm9jZWVkaW5nIHdpdGggdGhlIGxlc3Nvbi4gQWxzbywgYmUgc3VyZSB0byBmb2xsb3cgYWxvbmcgaW4gUiBTdHVkaW8uDQoNCjxpZnJhbWUgd2lkdGg9IjMyMCIgaGVpZ2h0PSIxODAiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvcV92MkMwS0hXU0kiIHRpdGxlPSJZb3VUdWJlIHZpZGVvIHBsYXllciIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgY2xpcGJvYXJkLXdyaXRlOyBlbmNyeXB0ZWQtbWVkaWE7IGd5cm9zY29wZTsgcGljdHVyZS1pbi1waWN0dXJlIiBhbGxvd2Z1bGxzY3JlZW4gZGF0YS1leHRlcm5hbD0iMSI+DQoNCjwvaWZyYW1lPg0KDQpUaGUgdmlkZW8gYmVsb3cgcHJvdmlkZXMgYSBtb3JlIGluLWRlcHRoIGRlYnVnZ2luZyBleGFtcGxlIGluIHdoaWNoIGZ1bmN0aW9ucyBhcmUgZGVidWdnZWQuDQoNCjxpZnJhbWUgd2lkdGg9IjMyMCIgaGVpZ2h0PSIxODAiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvUVF4VGYzbzA3TlUiIHRpdGxlPSJZb3VUdWJlIHZpZGVvIHBsYXllciIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgY2xpcGJvYXJkLXdyaXRlOyBlbmNyeXB0ZWQtbWVkaWE7IGd5cm9zY29wZTsgcGljdHVyZS1pbi1waWN0dXJlIiBhbGxvd2Z1bGxzY3JlZW4gZGF0YS1leHRlcm5hbD0iMSI+DQoNCjwvaWZyYW1lPg0KDQojIyBEZWJ1Z2dpbmcNCg0KVGhlIHByb2Nlc3Mgb2YgZGVidWdnaW5nIC0tIG9yIGZpbmRpbmcgbG9naWMgZXJyb3JzIGluIHByb2dyYW1zIC0tIGlzIGEgY29tcGxleCBzdWJqZWN0IGFuZCBvZnRlbiBtb3JlIGFydCB0aGFuIHNjaWVuY2UuIFlvdSB3aWxsIGZpbmQgdGhhdCB5b3UgYmVjb21lIGJldHRlciBvdmVyIHRpbWUuIFRoaXMgbGVzc29uIGZvY3VzZXMgb24gdGhlIGludGVyYWN0aXZlIGRlYnVnZ2VyIGFuZCBub3QgYXMgbXVjaCBvbiBnZW5lcmFsIGRlYnVnZ2luZyBwcmluY2lwbGVzLiBTZWUgdGhlIHJlZmVyZW5jZXMgZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gdGhhdCB0b3BpYy4NCg0KRGVidWdnaW5nIG1lYW5zIGV4cGVyaW1lbnRhdGlvbiwgaHlwb3RoZXNpcyB0ZXN0aW5nLCBlbGltaW5hdGlvbiwgdHJpYWwtYW5kLWVycm9yLCBhbmQgbG90cyBvZiBwYXRpZW5jZS4NCg0KPiBGaW5kaW5nIHlvdXIgYnVnIGlzIGEgcHJvY2VzcyBvZiBjb25maXJtaW5nIHRoZSBtYW55IHRoaW5ncyB0aGF0IHlvdSBiZWxpZXZlIGFyZSB0cnVlIC0tLSB1bnRpbCB5b3UgZmluZCBvbmUgd2hpY2ggaXMgbm90IHRydWUuXA0KPiBcDQo+IC0tLU5vcm0gTWF0bG9mZg0KDQpGcm9tIGFuIGFic3RyYWN0IHBlcnNwZWN0aXZlLCBkZWJ1Z2dpbmcgbWVhbnMgZmluZGluZyB3aGVyZSB0aGUgY29kZSBkb2VzIG5vdCB3b3JrIGFzIGV4cGVjdGVkLiBJdCBydW5zIGJ1dCBpdCBkb2VzIG5vdCBwcm9kdWNlIHRoZSByZXN1bHQgeW91IGV4cGVjdC4gVGhpcyBjYW4gYmUgZnJvbSBhbiBlcnJvciBpbiB5b3VyIHByb2dyYW1taW5nIGxvZ2ljLCBhIG1pc2ludGVycHJldGF0aW9uIG9mIGEgbGFuZ3VhZ2UgY29uc3RydWN0LCBvciBhIGRlZmVjdCBpbiBhIHBhY2thZ2UgZnVuY3Rpb24gb3IgZXZlbiB0aGUgbGFuZ3VhZ2UgaXRzZWxmLiBOYXR1cmFsbHksIHRoZSBmb3JtZXIgdHdvIGFyZSBtb3N0IGNvbW1vbiBhbmQgd2Ugd2lsbCBmb2N1cyBvbiB0aG9zZSBpbiB0aGlzIGxlc3Nvbi4NCg0KVG8gZmluZCB3aHkgeW91ciBjb2RlIGlzbid0IGJlaGF2aW5nIGFzIHlvdSBleHBlY3QsIHlvdSBnZW5lcmFsbHkgZm9sbG93IHRoZSBhcHByb2FjaCBiZWxvdyB3aXRoaW4gYSBkZWJ1Z2dlciAtLSBhbHRob3VnaCBhIGRlYnVnZ2VyIGlzbid0IHN0cmljdGx5IG5lY2Vzc2FyeSBidXQgdmVyeSBjb252ZW5pZW50LiBPbmNlIHlvdSBhcmUgdXNlZCB0byBhbiBpbnRlcmFjdGl2ZSBkZWJ1Z2dlciwgbm90IGhhdmluZyBvbmUgd2lsbCBkZWZpbml0ZWx5IGJlIGZlbHQuDQoNCjEuICBSdW4geW91ciBjb2RlDQoyLiAgU3RvcCB0aGUgY29kZSBhdCB0aGUgbGluZSB3aGVyZSB5b3Ugc3VzcGVjdCBhbiBpc3N1ZQ0KMy4gIEluc3BlY3QgdGhlIGNvZGUgZm9yIG1pc3Rha2VzLCBpbnNwZWN0IHRoZSBjb250ZW50cyBvZiBvYmplY3RzLCBsb29rIGF0IHRoZSB2YWx1ZSBvZiB2YXJpYWJsZXMNCjQuICBwcm9jZWVkIHJ1bm5pbmcgdGhlIGNvZGUgc3RhdGVtZW50IGJ5IHN0YXRlbWVudCBhbmQgcmVwZWF0IHN0ZXAgKDMpIGFmdGVyIGVhY2ggc3RlcA0KDQpJdCBpcyBvZnRlbiBzaW1wbGVyIHRvIGV4dHJhY3QgY29kZSB0aGF0IGlzbid0IHJ1bm5pbmcgaW50byBhIHNlcGFyYXRlICJzYW5kYm94IiAtLSBhIG5ldyBSIFNjcmlwdCB0aGF0IGp1c3QgY29udGFpbnMgdGhlIGZsYXdlZCBjb2RlLiBUaGF0IHdheSB5b3UgY2FuIGV4cGVyaW1lbnQgd2l0aG91dCBtZXNzaW5nIHVwIHlvdXIgYWN0dWFsIHByb2R1Y3Rpb24gY29kZS4NCg0KIyMgU2V0dGluZyBCcmVha3BvaW50cw0KDQpUbyBlbnRlciB0aGUgZGVidWdnZXIgeW91IG5lZWQgdG8gZW50ZXIgZGVidWcgbW9kZS4gQSBjb21tb24gd2F5IGlzIHRvIHNldCBhIGJyZWFrcG9pbnQgYXQgYSBsaW5lIGFuZCB0aGVuIHJ1biB5b3VyIGNvZGUuIEl0IHdpbGwgYXV0b21hdGljYWxseSBzdG9wIGF0IHRoZSBicmVha3BvaW50LiBUbyBzZXQgYSBicmVha3BvaW50IGF0IGEgbGluZSBpbiBSU3R1ZGlvW15sLTYtMTk1LTFdLCBjbGljayB0byB0aGUgbGVmdCBvZiB0aGUgbGluZSBudW1iZXIgaW4gdGhlIGVkaXRvciwgb3IgcHJlc3NpbmcgKlNoaWZ0K0Y5KiB3aGVuIHlvdXIgY3Vyc29yIGlzIG9uIHRoZSBkZXNpcmVkIGxpbmUuIElmIHRoYXQgYWxsIGZhaWxzLCB0aGVuIGdvIHRvIHRoZSBsaW5lIHdoZXJlIHlvdSB3YW50IHlvdXIgY29kZSB0byBzdG9wIGFuZCBzZWxlY3QgbWVudSBpdGVtICpEZWJ1Zy9Ub2dnbGUgQnJlYWtwb2ludCouIEEgcmVkIGRvdCB3aWxsIGFwcGVhciBuZXh0IHRvIHRoZSBsaW5lIHdoZXJlIHlvdSBoYXZlIGEgYnJlYWtwb2ludC4gVG8gcmVtb3ZlIHRoZSBicmVha3BvaW50LCBqdXN0IGRvIHRoZSBzYW1lIHN0ZXBzIGFnYWluIC0tIGl0IGlzIGEgdG9nZ2xlLg0KDQpbXmwtNi0xOTUtMV06IFNldHRpbmcgYnJlYWtwb2ludHMgYW5kIHVzaW5nIHRoZSBkZWJ1Z2dlciBpcyBvbmx5IHBvc3NpYmxlIHdpdGggUiBTY3JpcHRzIGFuZCBjYW5ub3QgYmUgdXNlZCB3aXRoIFIgTWFya2Rvd24gZG9jdW1lbnRzLiBUbyBkZWJ1ZyBSIE1hcmtkb3duIGNvZGUgY2h1bmtzIHlvdSB3b3VsZCBuZWVkIHRvIGNvcHkgdGhlbSB0byBhbiBSIFNjcmlwdCwgZGVidWcgdGhlbSwgYW5kIHRoZW4gY29weSBiYWNrIHRvIHRoZSBjb2RlIGNodW5rcy4NCg0KIVtdKHNldF9icmVha3BvaW50LmpwZyl7d2lkdGg9IjM1JSJ9DQoNCk5vdywgaWYgeW91IHJ1biB0aGUgY29kZSBpdCB3aWxsIHN0b3AgYXQgdGhhdCBsaW5lIGFuZCB0aGVuIHlvdSBjYW4gZ28gdG8gdGhlIENvbnNvbGUgYW5kIHByaW50IG91dCB2YXJpYWJsZSB2YWx1ZXMgYW5kIHJ1biAqYWQgaG9jKiBzdGF0ZW1lbnRzIHRvIGV4cGVyaW1lbnQuIFRvIHJ1biB5b3VyIFIgY29kZSBsaWtlIGEgcHJvZ3JhbSAoc3RhcnRpbmcgYXQgdGhlIGZpcnN0IHN0YXRlbWVudCksIHlvdSBuZWVkIHRvICJzb3VyY2UiIHRoZSBmaWxlLiBJbiBSIFN0dWRpbyB0aGF0IG1lYW5zIHlvdSBzZWxlY3QgdGhlICJTb3VyY2UiIGJ1dHRvbi4NCg0KIVtdKHNvdXJjZV9SLmpwZyl7d2lkdGg9IjM1JSJ9DQoNCk9uY2UgeW91IGFyZSBpbiBkZWJ1ZyBtb2RlIGFuZCB5b3UgaGF2ZSBzdG9wcGVkIGF0IHlvdXIgZmlyc3QgYnJlYWtwb2ludCwgeW91IGNhbiBzdGVwIHRocm91Z2ggdGhlIGNvZGUgbGluZSBieSBsaW5lLiBOb3csIHlvdSBuZWVkIHRvIGFwcGx5IHlvdXIgZGV0ZWN0aXZlIHNraWxscyBhbmQgeW91ciBleHBlcmllbmNlIC0tIGFuZCBub3QganVzdCBleHBlcmllbmNlIHByb2dyYW1taW5nIGluIFIsIGJ1dCBhbHNvIGFsbCBvZiB5b3VyIHByb2dyYW1taW5nIGV4cGVyaWVuY2Ugd2l0aCBvdGhlciBsYW5ndWFnZXMuIE1hbnkgb2YgdGhlIHNhbWUgaXNzdWVzIG9jY3VyIGluIGFsbCBsYW5ndWFnZXMuIEZvciBleGFtcGxlLCBpZiB5b3UgZ2V0IGFuIGVycm9yIHdoZW4gdHJ5aW5nIHRvIGxvYWQgYSBmaWxlIChsaWtlIGEgQ1NWIHdpdGggPGNvZGU+cmVhZC5jc3Y8L2NvZGU+KSB0aGVuIHlvdSBzaG91bGQgYWx3YXlzIHN1c3BlY3QgdGhhdCB0aGUgZmlsZSBjYW5ub3QgYmUgZm91bmQuIElmIHlvdSBkaWRuJ3Qgc3BlY2lmeSBhbiBhYnNvbHV0ZSBhbmQgZnVsbCBwYXRoIHRvIHRoZSBmaWxlLCB0aGVuIHlvdSBuZWVkIHRvIGFzc3VtZSB0aGF0IFIgdXNlcyBhIGRpZmZlcmVudCBkZWZhdWx0IGZvbGRlciB0aGFuIHdoZXJlIHlvdSBiZWxpZXZlIHlvdSBhcmUuIE9uY2UgeW91IGVudGVyIHRoZSBkZWJ1Z2dlciwgY2FsbCA8Y29kZT5nZXR3ZCgpPC9jb2RlPiB0byBmaW5kIG91dCB3aGVyZSBSIGlzIGxvb2tpbmcgZm9yIGZpbGUuIFRoZSBzaG9ydCB2aWRlbyBiZWxvdyBpbGx1c3RyYXRlcyB0aGlzIGNvbW1vbiBwcm9ibGVtIGluIFJbXmwtNi0xOTUtMl0uDQoNCltebC02LTE5NS0yXTogVGhlcmUgaXMgZXZlbiBhIHBhY2thZ2UgdG8gaGVscCB3aXRoIGZpbmRpbmcgb3V0IHdoZXJlIHlvdSBhcmUsIHRoZSAqKmhlcmUqKiBwYWNrYWdlLiBJdCBjYW4gaGVscCBtYWtlIGNvZGUgImxvY2F0aW9uIGluZGVwZW5kZW50Ii4NCg0KPGlmcmFtZSBzcmM9Imh0dHBzOi8vcGxheWVyLnZpbWVvLmNvbS92aWRlby83MjUyMzEyMTk/aD03YTc4Njc4M2JmIiB3aWR0aD0iMzIwIiBoZWlnaHQ9IjI4MiIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhdXRvcGxheTsgZnVsbHNjcmVlbiIgYWxsb3dmdWxsc2NyZWVuIGRhdGEtZXh0ZXJuYWw9IjEiPg0KDQo8L2lmcmFtZT4NCg0KVG8gZXhpdCBkZWJ1ZyBtb2RlLCB5b3Ugc2VsZWN0ICpEZWJ1Zy9TdG9wIERlYnVnZ2luZyosIHByZXNzICpTSElGVC1GOCosIG9yIGhpdCB0aGUgU3RvcCBidXR0b24gd2l0aGluIHRoZSBkZWJ1Z2dpbmcgY29udHJvbHMgYXMgc2hvd24gYmVsb3cuIFN0b3BwaW5nIG1lYW5zIHRvIHF1aXQgdGhlIGN1cnJlbnQgcHJvZ3JhbSBhbmQgc3RvcCBhbnkgZnVydGhlciBzdGF0ZW1lbnRzIGZyb20gZXhlY3V0aW5nLg0KDQohW10oZGVidWctY29udHJvbHMuanBnKXt3aWR0aD0iNDUlIn0NCg0KT2YgY291cnNlLCB5b3UgY2FuIGFsc28gdXNlIHRoZSBjb250cm9scyB0byBjb250aW51ZSB0byB0aGUgbmV4dCBicmVha3BvaW50LCBwcmVzcyAiQ29udGludWUiLCBvciBzdGVwIGludG8gYSBibG9jaywgbG9vcCwgb3IgZnVuY3Rpb24sIG9yIGV4ZWN1dGUgdGhlIHJlbWFpbmRlciBvZiBhIGxvb3Agb3IgZnVuY3Rpb24uIEV4cGVyaW1lbnQgd2l0aCBlYWNoIGNvbnRyb2wgdG8gc2VlIHdoYXQgaXQgZG9lcy4NCg0KUmF0aGVyIHRoYW4gc2V0dGluZyBicmVha3BvaW50cyBpbiB0aGUgZWRpdG9yLCB5b3UgY2FuIGFsc28gaW5jbHVkZSBhIGNhbGwgdG8gdGhlIGZ1bmN0aW9uIDxjb2RlPmJyb3dzZXIoKTwvY29kZT4gaW4geW91ciBjb2RlIHdoaWNoLCB3aGVuIGVuY291bnRlcmVkLCB3aWxsIHBhdXNlIGV4ZWN1dGlvbiBhbmQgcGxhY2UgeW91IGluIHRoZSBkZWJ1Z2dlciAtLSBpdCBmdW5jdGlvbnMgYXMgYSBwcm9ncmFtbWF0aWNhbGx5IHNldCBicmVha3BvaW50IGFuZCBjYW4gYmUgaW52b2tlZCBjb25kaXRpb25hbGx5Lg0KDQpUaGUgY29kZSBiZWxvdyBpbGx1c3RyYXRlcyB0aGlzLiBPbiBsaW5lIHRoZXJlIGlzIGEgY2FsbCBvbiBsaW5lIDYgdG8gdGhlIGZ1bmN0aW9uIDxjb2RlPmJyb3dzZXIoKTwvY29kZT4uDQoNCmBgYHtyIGV2YWw9RiwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcyd9DQpkZiA8IHJlYWQuY3N2KGZpbGUgPSBmbikNCg0KbiA8LSBucm93KGRmKQ0KDQppZiAobiA8IDEpDQogIGJyb3dzZXIoKQ0KDQpmb3IgKGkgaW4gMTpuKQ0Kew0KICAjIC4uLg0KfQ0KYGBgDQoNCiMjIFN1bW1hcnkNCg0KRGVidWdnaW5nLCB0aGUgcHJvY2VzcyBvZiByZW1vdmluZyBsb2dpYyBlcnJvcnMsIGlzIGFuIGltcG9ydGFudCBwYXJ0IG9mIHByb2dyYW1taW5nLiBBbiBpbnRlcmFjdGl2ZSBkZWJ1Z2dlciwgY291cGxlZCB3aXRoIGxhbmd1YWdlLWluZGVwZW5kZW50IGZvcmVuc2ljIHN0cmF0ZWdpZXMsIGlzIGEgdmFsdWFibGUgdG9vbCBpbiB0aGUgYXJzZW5hbCBvZiB0aGUgcHJvZmVzc2lvbmFsIHByb2dyYW1tZXIuIFRoaXMgbGVzc29ucyBwcm92aWRlZCBhbiBvdmVydmlldyBvZiB0aGUga2V5IGZlYXR1cmVzIG9mIHRoZSBpbnRlcmFjdGl2ZSBkZWJ1Z2dlciBidWlsdCBpbnRvIHRoZSBSIFN0dWRpbyBJREUuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBGaWxlcyAmIFJlc291cmNlcw0KDQpgYGB7ciB6aXBGaWxlcywgZWNobz1GQUxTRX0NCnppcE5hbWUgPSBzcHJpbnRmKCJMZXNzb25GaWxlcy0lcy0lcy56aXAiLCANCiAgICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LA0KICAgICAgICAgICAgICAgICBwYXJhbXMkbnVtYmVyKQ0KDQp0ZXh0QUxpbmsgPSBwYXN0ZTAoIkFsbCBGaWxlcyBmb3IgTGVzc29uICIsIA0KICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LCIuIixwYXJhbXMkbnVtYmVyKQ0KDQojIGRvd25sb2FkRmlsZXNMaW5rKCkgaXMgaW5jbHVkZWQgZnJvbSBfaW5zZXJ0MkRCLlINCmtuaXRyOjpyYXdfaHRtbChkb3dubG9hZEZpbGVzTGluaygiLiIsIHppcE5hbWUsIHRleHRBTGluaykpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFNlZSBBbHNvDQoNCi0gICBbNi4xOTEgRGVidWdnaW5nIFIgQ29kZV0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wNi5yL2wtNi0xOTEtZGVidWdnaW5nL2wtNi0xOTEuaHRtbCkNCg0KIyMgUmVmZXJlbmNlcw0KDQpbV2lja2hhbSwgSC4gKG4uZC4pIEFkdmFuY2VkIFIuIENoYXB0ZXIgMjI6IERlYnVnZ2luZ10oaHR0cHM6Ly9hZHYtci5oYWRsZXkubnovZGVidWdnaW5nLmh0bWwpDQoNCltEZWJ1Z2dpbmcgUiBDb2RlOiBXaGF0IHRoZXkgRm9yZ290IHRvIFRlYWNoIEFib3V0IFJdKGh0dHBzOi8vcnN0YXRzLnd0Zi9kZWJ1Z2dpbmctci1jb2RlLmh0bWwpDQoNCltNY1BoZXJzb24sIEouICgyMDIxKS4gRGVidWdnaW5nIHdpdGggdGhlIFJTdHVkaW8gSURFLiBEZWMgMjgsIDIwMjEuXShodHRwczovL3N1cHBvcnQucnN0dWRpby5jb20vaGMvZW4tdXMvYXJ0aWNsZXMvMjA1NjEyNjI3LURlYnVnZ2luZy13aXRoLXRoZS1SU3R1ZGlvLUlERSkNCg0KIyMgRXJyYXRhDQoNCk5vbmUgY29sbGVjdGVkIHlldC4gTGV0IHVzIGtub3cuDQoNCmBgYHs9aHRtbH0NCjxzY3JpcHQgc3JjPSJodHRwczovL2Zvcm0uam90Zm9ybS5jb20vc3RhdGljL2ZlZWRiYWNrMi5qcyIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij4NCiAgbmV3IEpvdGZvcm1GZWVkYmFjayh7DQogICAgZm9ybUlkOiAiMjEyMTg3MDcyNzg0MTU3IiwNCiAgICBidXR0b25UZXh0OiAiRmVlZGJhY2siLA0KICAgIGJhc2U6ICJodHRwczovL2Zvcm0uam90Zm9ybS5jb20vIiwNCiAgICBiYWNrZ3JvdW5kOiAiI0Y1OTIwMiIsDQogICAgZm9udENvbG9yOiAiI0ZGRkZGRiIsDQogICAgYnV0dG9uU2lkZTogImxlZnQiLA0KICAgIGJ1dHRvbkFsaWduOiAiY2VudGVyIiwNCiAgICB0eXBlOiBmYWxzZSwNCiAgICB3aWR0aDogNzAwLA0KICAgIGhlaWdodDogNTAwLA0KICAgIGlzQ2FyZEZvcm06IGZhbHNlDQogIH0pOw0KPC9zY3JpcHQ+DQpgYGANCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19kZXBsb3lLbml0LlInKSksIGluY2x1ZGUgPSBGQUxTRX0NCmBgYA0K