The trace shows nested events in this way. Since components in JUCE are in a hierarchy, if you trace a component’s paint as well as its children (or other function calls or wrapped code chunks in the code path) the nesting will be visualized.
I just have one question at the moment: If you have a slice selected, is there any way to advance to the next slice?
When slices are more than a few milliseconds apart, trying to move to the next one using scroll left/right is extremely tedious. Constantly scrolling in, out, panning left, right - can’t you just advance to the next slice?
I might be missing something, but so far I can’t find an answer…
I’ve never had that need. I just zoom to the level I care about and point and click.
It does seem like [
and ]
would be good candidates even if the context is not a ‘flow’. You could raise a GitHub issue with them, they’ve been responsive to me in the past.
One other cool feature I should mention are their “pivot tables”.
You can enable this under “Flags > Pivot tables V2”
This lets you highlight chunks and quickly get aggregates like min/max as well as see a data table of the individual events selected (by event name).
Thanks. Yes, I saw those. After playing with this a bit, I can say: this is really deep. And fascinating.
I took the tip of your blog post and put a trace in Component::paintComponentAndChildren():
// at top of juce_Component.cpp, outside of namespace:
#include <melatonin_perfetto/melatonin_perfetto.h>
void Component::paintComponentAndChildren (Graphics& g)
{
auto s = getName() + "::pCAC";
TRACE_EVENT("component", perfetto::DynamicString{ s.getCharPointer() } );
If you name all your components (as I do), you can easily see all of the nested painting and so on. Really interesting. I suspect this will be very useful in UI optimization.
Edit: great work on this, and thanks!!!
Nice! Glad you got the root component level stuff working too.
I’m hoping we can eventually get a per-component paint callback to hook into this stuff more easily/officially, see this Feature Request: FR: Callback or other mechanism for exposing Component debugging/timing
I voted for it.
Voted for it too. Thanks @sudara for opening this interesting discussion.
Actually it got me interested into macOS’s Signposts Really simple API, quite low overhead and very helpful. Building on @dave96’s class I came up with this simple wrapper, if anybody is interested:
#ifdef __APPLE__
#include <os/log.h>
#include <os/signpost.h>
struct Signpost {
Signpost() : log_(os_log_create("com.company.myApp", OS_LOG_CATEGORY_POINTS_OF_INTEREST)) {}
static os_log_t& getInstance() {
static Signpost signpost;
return signpost.log_;
}
private:
os_log_t log_;
};
#define SIGNPOST_BEGIN(name, ...) \
os_signpost_id_t _signpost_interval_id = os_signpost_id_generate(Signpost::getInstance()); \
os_signpost_emit_with_type(Signpost::getInstance(), OS_SIGNPOST_INTERVAL_BEGIN, \
_signpost_interval_id, name, __VA_ARGS__)
#define SIGNPOST_END(name, ...) \
os_signpost_emit_with_type(Signpost::getInstance(), OS_SIGNPOST_INTERVAL_END, \
_signpost_interval_id, name, __VA_ARGS__)
#define SIGNPOST_EVENT(name, ...) \
os_signpost_emit_with_type(Signpost::getInstance(), OS_SIGNPOST_EVENT, OS_SIGNPOST_ID_EXCLUSIVE, \
name, __VA_ARGS__)
#else
#define SIGNPOST_BEGIN(...) ;
#define SIGNPOST_END(...) ;
#define SIGNPOST_EVENT(...) ;
#endif
Caveat: with the new (post-10.14) “named” signpost API I don’t think you can go RAII like @dave96, because the strings attached to the events must be litterals.
Then to e.g. profile all paint calls it’s a matter of temporarily modifying juce_Component.cpp
:
diff --git a/modules/juce_gui_basics/components/juce_Component.cpp b/modules/juce_gui_basics/components/juce_Component.cpp
index 9f8d9d742..5063604aa 100644
--- a/modules/juce_gui_basics/components/juce_Component.cpp
+++ b/modules/juce_gui_basics/components/juce_Component.cpp
@@ -23,6 +23,21 @@
==============================================================================
*/
+#include <cxxabi.h>
+
+std::string demangle(const char* name) {
+
+ int status = -1;
+ std::unique_ptr<char, void(*)(void*)> res {
+ abi::__cxa_demangle(name, nullptr, nullptr, &status),
+ std::free
+ };
+
+ return (status==0) ? res.get() : name ;
+}
+
namespace juce
{
@@ -1996,6 +2011,7 @@ void Component::paintWithinParentContext (Graphics& g)
void Component::paintComponentAndChildren (Graphics& g)
{
+ SIGNPOST_BEGIN("paint", "%s", demangle(typeid(*this).name()).c_str());
auto clipBounds = g.getClipBounds();
if (flags.dontClipGraphicsFlag && getNumChildComponents() == 0)
@@ -2057,6 +2073,7 @@ void Component::paintComponentAndChildren (Graphics& g)
Graphics::ScopedSaveState ss (g);
paintOverChildren (g);
+ SIGNPOST_END("paint");
}
void Component::paintEntireComponent (Graphics& g, bool ignoreAlphaLevel)
… which gets me this beautiful waterfall on my app
@sudara hope we get a more permanent way of profiling Component
!
Well, I managed it to create an RAII C++ wrapper class template that holds the event name string as template argument. In code it basically looks like
ScopedSignpost<"Name"> scopedSignpost;
It requires C++ 20 and directly accesses the internal _os_signpost_emit_with_name_impl
function – but it seems to work fine. I’ll see if I can manage to brush it up a bit and then share it here.
Hey Sudara, awesome work! This is extremely helpful. I got it to work pretty fast in Clion with CMake. I do have one issue, though. While the TRACE_DSP and TRACE_COMPONENT work just fine, I am getting a compile error for TRACE_EVENT_BEGIN(“a”, “b”) :
constexpr variable ‘kCatIndex_ADD_TO_PERFETTO_DEFINE_CATEGORIES_IF_FAILS_62’ must be initialized by a constant expression
Any ideas? Thanks again!
Glad it’s useful!
You can only use predefined categories as the first argument. So: dsp, component (unless you manually modify the module to add more)
Understood, thanks for the quick reply!
Hi again, Sudara - I have just ran this on Windows in CLion, and, while the Mac version runs just fine, on Windows it’s complaning that
cmake-build-debug/_deps/perfetto-src/sdk/perfetto.cc:69903:10: fatal error: afunix.h: No such file or directory
Any ideas? Thanks again!
Interesting, are you on the latest commit from the main branch?
We have tests in CI running both windows and mac for both compiling and tracing (thanks again to @benvining) — they are green right now, maybe you could share what toolchain you are using?
Edit: just confirmed the latest main
builds happily for me on CLion Windows w/ VS 2022 (17.0)
That header name sounds like it’s a Unix header… Are you using something like WSL, Cygwin, or MinGW?
AFUnix comes to Windows (10):
Seems there is a missing (expected) SDK to install … a missing “git submodule update --init --recursive” somewhere, maybe?
Thanks for all the replies and tips! Before these errors I did a full clean of my laptop and CLion defaulted to MinGW. Switching back to VS 2022 fixed it! Thanks!
I’ve just updated melatonin_perfetto to version 1.1. Please give it an update if you use it!
Before today, in some cases, TRACE_DSP
and TRACE_COMPONENT
macros did some string manipulation at run-time (only when profiling with PERFETTO=1
, of course).
That’s now fixed, and everything happens at compile time.
We went on a ridiculous constexpr adventure and I wrote the hackiest C++ code I’ve ever written.
String manipulation at compile time is pretty sketchy, even in C++20 (melatonin_perfetto supports C++17) and debugging constexpr/eval stuff is like going on a treasure hunt. If you want to get nerd sniped: I’m wrapping string literals returned by PRETTY_FUNCTION and friends in lambdas so they are usable as template parameters, can therefore be “unique” and manipulated at compile time. I could find no other way to trim strings at compile time in a way that was compatible with perfetto. Happy to be proven wrong though!
Anyway, it passes tests, and I’ve manually confirmed compile-time Debug/Release behavior on Clang/MSVC.
I moved my project from Mac to Windows today (having not compiled with Perfetto on Windows up to now). When I try to compile on VS 2019, with my Projucer Settings specifically set to C++17, I am getting the following fatal error:
Error C1189 #error: Perfetto is exploring a switch to C++17 in v34 (Feb 2023).
During this transitionary period, we are throwing an error when compiling Perfetto
with a standard less than C++17. Please reach out to perfetto-dev@googlegroups.com
if you have objections or thoughts on this move. To continue compiling this release of
Perfetto with C++11/14, specify the define PERFETTO_ALLOW_SUB_CPP17.
*Note*: this define *will* stop working in v34 (Feb 2023).
(compiling source file ..\..\..\..\modules\perfetto\sdk\perfetto.cc)
XXXXX-apphost_App C:\Users\sk\DocumentsXXXX JUCE (Win)\NO BU\XXXX JUCE 044 Win\modules\perfetto\sdk\perfetto.h 338
According to this, it seems to think that I am using LESS THAN C++17. But I’m not:
Hey Stephen,
Yes, we ran into this (fun/ridiculous/baffling) problem as well. Basically, the version detection in is broken in MSVC unless you pass the /Zc:__cplusplus
flag, see this link.
CMake doesn’t help out by default either, so in CMake one would need to do this in addition to setting the project as C++17/20:
target_compile_options("${PROJECT_NAME}" PUBLIC /Zc:__cplusplus)