[MUSIC PLAYING] FILIP HRACEK: Hi, I'm
Filip of Developer Advocate on the Flutter team. And I'm here today with Kenzie. Hi, Kenzie. KENZIE SCHMOLL: Hey, Filip. I'm Kenzie. I'm a Software Engineer on the
Flutter Developer Experience team. FILIP HRACEK: Kenzie is
actually one of the engineers behind DevTools. And that's what we're
talking about today. So that's fortunate. Kenzie, what are DevTools? KENZIE SCHMOLL: So, DevTools
is a tooling suite for Flutter and Dart developers consisting
of layout inspection tools, performance
tools, memory tools-- really just all
the debugging tools that you need to be an
efficient and effective Flutter developer bundled into a
single web suite for you. FILIP HRACEK: Oh,
so the DevTools have been around for a while. But what you're showing
is a little different. KENZIE SCHMOLL: It is
a little different. We actually rewrote
the entire suite and added some cool and new
features to the tooling suite. And the best part is we
rewrote it entirely in Flutter. FILIP HRACEK: So it's Flutter
tooling written in Flutter running in a web browser. KENZIE SCHMOLL: Yes. FILIP HRACEK: Do you use-- do you use DevTools to
debug DevTools, Kenzie? KENZIE SCHMOLL: I do. I do use DevTools
to debug DevTools. And it's quite useful. FILIP HRACEK: Awesome, awesome. Let's show how you
actually get to DevTools. In Android Studio,
there is this-- if you run your app in
either debug or profile, you have this little icon
which you can click on, and it opens up DevTools in
your favorite web browser. If I was using VS Code, there's
this handy little notification here. But also if I was actually
running the app from here, there would be a similar-looking
icon that I could click. So it's pretty easy to get to. And even if you're running
from the command line, there's ways to open it up
from the command line, which I will not be doing today. I wanted to show you-- or I wanted to
start with the app that we'll be trying to
debug or troubleshoot today. And the app is
called "Sloth App." And it's something that I've
been using for a while now. It is just terrible. It's full of issues and full
of performance inefficiencies and stuff like this. And that is intentional. And so I want to
show you how you would use DevTools
to make things right in an app like that. Hopefully your apps
are a little better. So the app is
structured like this. I have it here on my phone. And it's just connected to my
screen so that you can see it. But the way it's structured
is that there is always some issue on every third
page of this page view. And the reason for that is just
so that the different issues don't interact with each other. So they are separate, right? So let's have look
at the first issue, and that's actually
a layout issue. So here, you have, "Flutter
is Google's UI toolkit for building beautiful,"
and that's cut off. And that's not great. It seems like a layout
issue, but maybe I want to look at the code. Maybe it's a little hard to
parse what exactly this means. Kenzie, how can
DevTools help there? KENZIE SCHMOLL: So to
solve this problem, we're going to want to use the
Flutter inspector in DevTools, which is our tool for
helping you diagnose and debug layout
issues and just getting an overall understanding of the
general layout of your Flutter application. So here, we can see I
have the same app running that Filip does. And I am running in Debug mode
so I can see these errors. And we see the
all-too-familiar caution tape error for render overflows. And we want to use the
inspector to figure out why that's happening
and how we can fix it. So, I have the row selected
that contains the Flutter logo and the padding. If I enable that, you can
see this is the overall row. And what I can do is look
in the summary tree here-- that gives me just the overall
view of my widget hierarchy-- find the widget I'm looking
for, which is this row, and then jump over
to the Details side of the inspector,
which allows me to look at specific
properties for that row, as well as the properties
for children of the widget and so forth. So what we could do
is look through here and try to look through the
renderObject constraints and see if something's
unconstrained or if there's something
that we can fix. But that's complicated
and not very friendly. So we will use the
Layout Explorer, which is a feature that
we added to the Flutter version of DevTools
which was just released. And it's an example of where
building our own app in Flutter allowed us to build some
really cool features that would have been more difficult
in the previous version. So in this Layout
Explorer, I can tweak certain values of, say,
axis alignment or flex values. And I can see the
change hot reloaded into my running application. So this allows me to
kind of plug and play with different
properties and see if there's anything I can fix. So I'm going to
play with properties and see if it fixes my problem. That didn't, so
I'll put it back. And there we go. The render overflow
error goes away, and I now have my text widget
in this application where the padding widget containing
the text has a flex value so the row knows to give the
remaining space to that padding widget. And now I can go back
to my application and make that change. Does that help, Filip? FILIP HRACEK: Absolutely, yes. Let's actually show
how that would work. So I have the code here. It's a Flutter
logo and a padding. And then I can just use
another DevTool thing-- I mean, develop tool thing which
is the wrap with the widget. And I can use either
Flexible or Expanded here to give it the flex value
that Kenzie did in the UI. And I think the
key thing here is that Kenzie could play
with it without actually touching the code. And it's-- at least for me, it's
much more understandable if I said in the Layout
Explorer that, OK, so these children want to
take this much time and make-- it's not this much time-- this much width. And they can, because
there is just not enough. And you can also see, if you
click on the padding or text, it's not flexible, which coming
from a different framework, you might be like,
oh, but text should be kind of flexible, right? But no, it's by design. It's not unless you put it into
a flexible widget like Expanded or Flexible here. So it explains and also
really quickly points you to the right direction. So I [INAUDIBLE]. All right, let's have a
look at the next example. And the next example is
a performance problem, my favorite kind of problem. So I'll turn on the
performance overlay. And I'll just go
here and edit-- it should be a pretty
normal-looking infinite list, right? But then at some
point, you see that-- I don't know if you actually
see the jank of the scrolling itself. But you can definitely
see that spike there in the performance overlay. And that happens
for some reason. And again, I might want
to look at the code. But imagine the code
is very complicated. I would love to find out what's
going on through WebTools. So, Kenzie, how can
WebTools help here? KENZIE SCHMOLL:
Yeah, so let's look at the timeline in DevTools
to solve this problem. So here in the
timeline, I have data loaded from the same app and
scroll that Filip just showed. And I have a few different
parts of this page to explain. So the top chart here is
the Flutter frames chart, which basically takes all
the timeline activity that's coming in from your
Flutter application and breaks it down
by Flutter frames. So you can see you when
you have a single janky frame or a single
frame that maybe missed the budget you need to hit in
order to achieve a high frame rate of 60 FPS. And so for this
example, we can see that there's clearly one culprit
frame that is taking too long. And that's where
we would probably want to look to find
our performance problem. So if I select
this frame, that's going to zoom these
timeline events below to the timeline events
that correspond to that frame that I selected. So in this chart,
we have events that are asynchronous events either
from the Flutter framework or HTTP events that
come in, or even events that you sent yourself from
the Dart developer APIs. And then we have some
synchronous events that also come in on either
the Dart threads, the UI thread, or events that
pertain to the raster portion of drawing
a Flutter frame, so from the Flutter engine
and similar of like that. So for this example,
we can see that it's the UI portion of the
frame, or the Dart code, that's hogging CPU and
taking too much time. And we can obviously
see that's taking time. But we don't really
have a good idea of what exactly is happening
within this chunk of events that we need to solve
in our application. So to solve this,
we're actually going to use a feature called
Track Widget Builds. So I'll clear the timeline and
enable this Track Widget Builds feature. And basically what that does
is when we are receiving events from the Flutter framework,
it's actually sending events for each widget that was
built during the time that the build portion of a
Flutter frame is occurring. And so not only do we know, hey,
the build time of this frame took a long time. We can then dig further into
which specific widgets were being built. So here is the long frame again. I selected it. And I'm zooming to these
events in the chart. And it looks like we are
building MyExpensiveWidget, and it's taking
quite a long time. And we have some normal
widgets that are occurring, but these build times
aren't really concerning. If I had to take
a wild stab at it, I'd say that
MyExpensiveWidget is probably our expensive widget. What do you think, Filip? FILIP HRACEK: I forgot all
about MyExpensiveWidget. It is a widget that, if
you look at the code, is actually building a
stack of normal widgets that is 100 items deep. So it's basically one normal
widget but a hundred times overlaid on top of
each other, which is not great for performance. And it's also totally absurd. But it's-- again, I needed
some example to show that even things like that, even
without looking at the code, Kenzie could find out, oh,
there is a problem here, and it's called-- the widget is called something. And I can look through
my code and see what's going on there, right? So that's super helpful. I would probably say that
Check Widget Builds is disabled by default for a reason. It takes resources for
the profile mode app to track all these
widgets and their builds. So you don't want
to have it on always because it will make your
profile-built apps slower than they actually should be. And it interferes with
your measurement and stuff like this. But it's really,
really helpful to be able to open it
up once in a while or turn it on so
simply in the DevTools and then drill down
to what exactly which widgets take what
time and what they contain. So that's super helpful. All right, let's have a
look at the third problem, and I think our last. And look, it's
another scrolling-- infinite scrolling view. And this one is actually
janking all over the place. It's not just one widget there. But it's just
generally not great. And as you can see here in
the performance overlay, or maybe even just by
looking at the scroll, it's not super smooth, right? So I would like to know
what's going on there. And I can, of course,
look at the actual code. So in this case, I have
just a listView.builder. That's great. I can look at the
item, and I see that this is all
very-- you know, so like you have Container,
Row, SizedBox, Expanded, Text. Like, there's nothing that could
indicate that this thing is slow in any way. And I could go deeper and
deeper and read the whole thing. But again, it
would be super nice if I could just look at
DevTools and let it tell me where I should look first. So Kenzie, can you
help with that? KENZIE SCHMOLL: Yes. Yes, DevTools can
definitely help here. So here's a recording
of what Filip just showed in the timeline again. But in this case, I'm
not seeing anything jumping out at me that says,
hey, you have a janky frame. Go fix what's happening
in a single frame being built in your app. This is a situation where we
have a performance problem, but it's probably
over a span of frames rather than happening in
a single Flutter frame. So the timeline may not
be the best tool for this. And what we can
probably do is just use a traditional CPU profiler
to figure out what's going on. So what I'll do
is I will record. And then on my phone,
I'll do the same scroll that Filip showed. Stop the recording,
and now I have a CPU profile for what I just
recorded in my application. And what that
looks like for this is we have a few different
visualizations we can show. So this is the
Flame chart, which will give you a kind of
top-down visual representation of that CPU profile where each
one of these little samples is a sample of what the call
stack looks like at any given time that we took it. So then we aggregate them
all together and then put it into a nice Flame
chart graph for you to spot out hotspots
within this graph. We also have a Call
tree, which is basically the same thing as
the Flame chart, but instead of using a
graph, we use a table view. And this is going to help
you identify expensive paths. You can see that 63% of the
samples we took began with _drawFrame, which
is to be expected. We were just scrolling
through a list. But this isn't going to help
us identify expensive methods. What we want to do for that
is use the Bottom Up chart. So if I go to the Bottom Up
chart, it'll sort by the time that the CPU profiler
or the CPU was actually spent in a single method. So when you take a
sample, whatever's on top of that
call stack, that's where the CPU is actually
spending its time. And we had a lot of our samples
in this method right here, fibonacci, which, just
to take a guess at what that method does-- and to look here, it's
probably recursive. And we're probably
spending too much time doing something with fibonacci. So does that help, Filip? FILIP HRACEK: Oh, yeah. Yeah, it turns out I am
actually using fibonacci. But I want to show that even if
I didn't know my code at all, just looking at the method
name, I could just go and say, hey, what are the
fibonacci methods that I am calling in my app? And there's actually two. But if I look at this one, it's
actually in the same item line. And it turns out that I am
computing Fibonacci numbers for every of these subtitles
for no particular reason except to be terrible. So it's obviously not
a good thing to do. I just want to,
again, make a point here that even though this
example may be absurd, there are other things
that you as a developer want to do that are
computationally expensive. And you sometimes do them in
build methods without even realizing it, right? Like, you're parsing JSON. You're parsing Markdown. You're doing all this stuff that
might be like regex and stuff like this. And from the top-- that line view, it doesn't
seem that there is any problem. And it's really hard
to find out, like, why is my app slow here? But if you look at the
Bottom Up table, you can see, oh, it's actually
this particular method is taking this many
milliseconds every frame. Where is it called from and why? And then you can deal with it. So the way you deal with this
particular problem is just don't show Fibonacci
numbers in subtitles. But in other places, you
could cache these things. You could precompute them
before the user even comes here. But you can also kind
of spread the work through different frames. So sometimes, you do have to-- I don't know-- parse a
little bit of something. Then you don't maybe need to
parse all of it in one frame. And third, and probably
the best option, if it's really something
computationally intensive, is it just go and
make that computation on a different thread. So use .isolate, and
that's very easy. My computer almost fell down. But that's OK, I saved it. So yes, you can do this. And it's very helpful to be
able to look at the CPU profile. All right, so Kenzie, I think
we are almost at a time. This is it. But could you show
us some other things that DevTools can do for us? KENZIE SCHMOLL: Sure. So I've already showed some
of the layout inspection tools as well as performance
tools in DevTools. But we also have tools for a
variety of other use cases, such as the Memory page. So the Memory page allows me
to see a live view of both Dart and Android memory. So I got my native memory on the
right and then the Dart memory over here on the left. And as I do things
in my application, the memory increase
will show in the graph. Or as garbage
collections happen, those will show in
the graph as well. And I can take snapshots
to get a live view of what my heap looks like at the
time I took the snapshot. So what I just showed
was the Heat Map view where we can zoom into the heap
and check out what's going on and what's hogging the memory. Or we can look at it in a table
view and just kind of jump into the classes and
objects that are allocated and how much space
they're taking. So Filip showed earlier
that each page is separated by a different GrayPage. So you can see we have
11 instances of GrayPage and only one of the GreenPage. So that reflects
what we would expect. And then you can go even further
and look at the instances to see if there is any notable
information in those instances. So that's the Memory page. We also have a
Network page, which collects HTTP requests that
come in and allows us to inspect those to check out their
headers, their properties for the response and request,
as well as any timing data. So when was it initiated? When was it received? And this is a lot easier
than doing print debugging. So if I have a network
request coming in and I want to check out
the isolateId, for example, OK, I can print that. But then what if I also want
to see the contentLength? Well, I can print that too. But what if I also want
to see the reasonPhrase? And you can use
your imagination. Printing a lot of properties
for an object like this can be time consuming. And wouldn't it be nice if
there's just a nice network inspector for you to look at all
those properties in our tool? So that's what this is. So that is the Memory page,
the Networking page, and then a couple other things to show-- we have a full
source-level debugger. So I can set a breakpoint-- I'll set a breakpoint
here, which should be hit. And I can step over and in and
through, just as we'd expect. I can also look at
the variables that are available at the breakpoint
and inspect those properties and values. And I can see print
logs that come in. Anything that's
coming to the console will come directly to me. I can look up other classes
and files in my code. So let's maybe look
at the green screen. And here's the green
screen in the app. And I can inspect that code and
just read through the source there. Let's resume and get
out of the debugger. And then we also
have a Logging view that listens for logs coming
into your application, whether those are GC events or
just regular console prints, or even logs that
you've tagged yourself. So here is a log that
Filip gave a special tag. The Flutter framework's
not the only one that can make nice tagged logs. So he did that. And let's say I only want to see
my_network_logs that come in. I can filter for those
in this Logging view and only see those requests. Or let's say, I don't want to
see anything from GC events. So I can do a negative
filter and get rid of those. So that's kind of fun. Another thing to call
out in this Logging view is see these Flutter
frame events. Because we're displaying this in
Flutter and not just a console, we can do creative
things like give you a visual representation of
how long the frame took. So here's a Flutter frame that
took 52 milliseconds to render and one that took 31. And so you can see the
proportional difference between this bar visualization. So again, something
that's just-- we were able to
do in Flutter that was really easy and elegant
and makes it hopefully easier for you to understand and read. So that's a brief overview. Hope that helps and
gives you a good idea of what we have to offer. FILIP HRACEK: Thanks. OK, so I want to
encourage everyone to go to flutter.dev and look
at the DevTool documentation. There's a bunch of
pages right here. So you should read all those. But maybe more importantly,
just fire up DevTools, see what's new there, and try
to play with it, because I guarantee you that you don't
know about all the things that are available. And many of them
will be super useful at some point in your future. So, all right. I think-- well, I want
to thank you, Kenzie, for both showing us today
what DevTools can do and for building these
tools in the first place. And I want to thank you,
audience, for watching. KENZIE SCHMOLL: Thank you. [MUSIC PLAYING]
I guess I'll start using those tools now :) pretty dope actually