Introduction

AppleCake is a profiler for Love2D. It uses Chromium's trace tool to show you your profiling data. You can get to the tool by going to about://tracing. You can only access the tool through a Chromium browser! Chrome and Edge have been tested to work.


Features
  • Profile how long code takes
  • Nested Profiling, so you can get more detailed information
  • Mark timeless events when they happen
  • Trackvariables on a bar graph as they change
  • View variables later in the trace tool
  • Profile Lua's memory usage in a bar graph format
  • Multi-threaded profiling support
  • Disable for release easily
  • Recover Crashed data with ease
  • Switch to and from jprof easily so you can try AppleCake out in your project

Quick Example

A short example displaying it's basic usage in your project, so you can get a feel for it.

Once ran it will produce a *.json that can be dropped in to the tracing tool chrome://tracing. You can find the resulting file for the above code produced here. It should look like the following.

Quick look at what the data will look like.

Fully Featured Example

This example has a lot more detail than the previous to show it's full features.

Tutorial

Tutorials on how to use AppleCake effectively.

Installation

Run git clone https://github.com/EngineerSmith/AppleCake in your project, suggested to do it within a lib folder, but it's up to you how your project is structured. You could otherwise download the zip from the GitHub page and unzip it.
The project contains a init.lua so you can directly require the project folder.

Require

AppleCake returns a function when required, this function has a parameter that will be saved for the entire project. By default it's set to true, the details are as followed.

Warning

Be aware that this setting is set across threads, which allows you to require AppleCake in threads without having to set if you're within debug mode. Only the first require ran will set if AppleCake is active or not for all threads. So you can freely do require("AppleCake")() and get the correct table for the first set mode.

AppleCake uses less memory and processing when turned off, but any table created to put args into AppleCake profiles or marks will still be active. You can use the variable appleCake.isActive to find out if AppleCake is active or not if you wish.

Getting started

The most basic set-up is as followed. This will write all profiling data to the same file. You can have multiple sessions setup if you wanted to break up your profiling data, but only one can be active at a time.

Once the session has began, you can start profiling. You can use profiling like this to see how long your functions take and what they do.

There are also marks to add a timeless event, such as when your game switches state. You can also measure how much memory Lua is currently using at any given point.

A recently new feature in AppleCake are counters. These produce a bar graph in the profile viewer to track a variable's change over time

If you're having performance issues with AppleCake in a thread. You can buffer profiling data in that thread to send it all at once. This is handy for threads that are constantly producing a lot of profiling data per frame, such as the main thread. Profiles, marks and counters are all buffered.

These are examples of AppleCake's main functionality, check out the individual documentation on each function for further details of their use with more examples.

Threading

This section will show an example how to profile different threads of your project. This could be used to explain how to use AppleCake in other files than the first file you require it in.
You want to require and set debug level in your main thread otherwise you will not have control over beginning and ending a session.

Warning

As AppleCake requires love.timer, it will require the module if it is enabled; if appleCake is disabled it will not load love.timer.

Tracing Tool

This will try to explain how to use the tracing tool, if you have any questions contact me through Love2D's Discord server (@psmith#4628).

First you will need to go to about://tracing where you can drag the created JSON into the website. This should read the file and then display it, otherwise it will tell you if there was any problems with the file.

Shortcuts

It's vital that you learn how to navigate around your data. You can view all the shortcuts by pressing the ? in the upper right corner of the page or pressing ? on your keyboard. We will go over how to use certain tools to see more details, but have a play around and learn.

As you may have a different version, it's recommended to check your own page for shortcuts.

Basic View

This will be the view once you drop your JSON into the tracing tool. You can use the first tool to select parts of data, Click and drag over a row(s)Shortcut: 1. This will give a breakdown of what you've selected and how long they took. You can click on the individual names to see all the currently selected ones of that (Not the magnifying glass). You can use this view to then compare args of the selected.

Note

All time is given in MS, microseconds

Marks

There are two types of marks, "p" or "t". "p" will add a line across the graph (Blue circle). While "t" will add a timeless slice (Red circle). You will need to click and drag to select Marks unless you have amazing aim.

Zoom

You can zoom into a single frame using Shortcut 3 and use the previous drag and click tool to select just the profiling data for that frame.

Args/Graphs

You can click any section or mark to view it's arguments, but you can also compare selected items. Once selected, press the Start radio button to get the following view. You can also press Duration to get a break down of how long a profile took.

Counter

You can view the bar graph easily as it is shown as a separate row. Ypu can click on a bar to see it's current value, or select a few to see their values in order.

File Recovery

You can recover some profiling data even if endSession wasn't called. As AppleCake flushes every time it adds a profile, marker or counter to the file, you can drop it into the tracing tool about://tracing. It should be able to read what data has been written, unless the file has become corrupted. It will tell you what is wrong with the file, you might be able to open it and fix the JSON formatting yourself. The file is formatted as a JSON array.

Functions

Documentation on functions available in AppleCake.

.beginSession([filepath, name])

Must be called to start writing profiling to file. Doesn't return anything. Can only be called on the thread that first required AppleCake and only one session can be active at a time.

Parameter Optional Description
filepath Defaults to profile.json. It's the filepath to the file you want written to, it will create or overwrite anything in it's place.
name this parameter names the process and the current thread. Defaults to the project's identity if it has one. See .setName for more information
Example

.endSession()

Must be called to close the file correctly. This function ends and waits for it's thread to re-join. So calling this function can be slow if there are a lot of profiles or marks waiting in the queue, but it will wait until they have all be written before continuing. If .setBuffer is set to true and there is buffered data waiting to be flushed, it will flush before ending the session.

Tip

It's possible to recover some session data if it wasn't ended, check out File recovery.

Example

.setBuffer([enable])

By default nothing is buffered. Buffering is recommended to get a performance increase from AppleCake, but has the disadvantage that data not .flushed won't be saved. Such as when the program crashes unexpectedly. Only Profiles, Marks, and Counters are buffered while beginning and ending a session or setting thread name, or sort index will be force pushed.

Parameter Optional Description
enable Defaults to false, and only excepts true as a true value.
Example

.flush()

Flush any data buffered. Data is only buffered when .setBuffer is set to true. .endSession will also call flush if there is any buffered data waiting to be sent. Buffering data can have improvements to processing speed, but the draw back of increased memory usage. It's recommended to call flush once the frame is finished.

Example

.enable([levels])

By default this function will enable everything. You can use this function to fine tune what to disable, or disable everything.

Parameter Optional Description
levels Defaults to "all", which will enable everything. Options: "all", "none", "profile", "mark", or "counter". These Options will enable the one you select. You can pick multiple as shown in the example.
Example

.profile(name, [args, profile])

This function creates a new profile table, or reuses the table passed in. Use a mark to mark timeless events than creating a profile and stopping it instantly.

Parameter Optional Description
name Name of the profile, this is so you can identify the profile later in the viewer.
args Defaults to nil. Args is a flat table of values (string and number types). Args can be changed until :stop() is called.
profile Defaults to nil. Reuse a previous profile, to avoid creating new tables, this is the recommended way to use AppleCake.
Example

.profileFunc([args, profile])

This function creates a new profile table, or reuses the table passed in. It generates the name of the function it is currently running in for you.
For example, if you were profiling function love.update(dt) it will give update@main.lua#XX XX being the line it is declared on. func@file.lua#line Note that won't say the path of the file. In the event of profiling an anon function, it will use the function's memory address as the function name.

Parameter Optional Description
args Defaults to nil. Args is a flat table of values (string and number types). Args can be changed until :stop() is called.
profile Defaults to nil. Reuse a previous profile, to avoid creating new tables, this is the recommended way to use AppleCake. As the name is reused instead of regenerating it, you have to pass in the same profile than reusing it for multiple profiles.
Example

profile:stop()

This function can only be called from created profiles. It can only be called once, it has to be passed back into a profile function to reuse it.

.stopProfile(profile)

This is the same function that is stored in profile.stop. It has been added for ease of access if you wished use it or access it. appleCake.stopProfile(profile)

Example

.mark(name, [scope, args])

This marks a point, useful to show events triggering than measuring time. Args will show exactly like args do in normal profiling, but as this function marks a timeless event they cannot be changed once the function has been called as it doesn't return anything.

Parameter Optional Description
name Name of the profile, this is so you can identify the profile later in the viewer.
scope Defaults to "p". It can be set to either "p"(process) or "t" (thread). It's recommended to use process most of the time otherwise it can be hard to identify the mark in the tool. I would suggest using thread when it's an event you don't need to easily identify when looking at the data. See Marks for further details.
args Defaults to nil. Args is a flat table of values (string and number types).
Example

.counter(name, [args, counter])

This adds a variable that is tracked over time as displays it as a bar graph. The args are what is tracked and made into a bar graph, if there are multiple arguments then it will overlap the graphs.

Parameter Optional Description
name Name of the counter, this is so you can identify the profile later in the viewer. This must be consistent to see the previous counter calls link together.
args Defaults to nil. Args is a flat table of values (string and number types).
counter Defaults to nil. Reuse a previous counter, to avoid creating new tables, this is the recommended way to use AppleCake. As the counter is reused instead of regenerating it, you have to pass in the same counter than reusing it for multiple profiles as you won't be able to stop the correct profile then.
Example

.countMemory([memoryScale])

This function uses a counter to record memory in a graph that can show the growth and change. You can optionally set the output of the scale it uses with the options being "megabyte" or "byte" with the default being "kilobyte"

Warning

This function replaced the previous .markMemory([scope]) which has been deprecated.

Parameter Optional Description
memoryScale Defaults to "kilobyte" if it isn't set to one of the options being "megabyte" or "byte"
Example

.setName(name)

Set the name of the process across the whole project. This will also set the name of the current thread. You can speficiy a name within beginSession, which defaults to the filesystem identity.

Parameter Optional Description
name The name of the process and the current thread
Example

.setThreadName(name)

Set the name of the current thread. The default name is a random interager used to identity the thread internally with AppleCake.

Parameter Optional Description
name The name of the thread
Example

jprof

Documentation on functions available in AppleCake that match the jprof API.

Example

.jprof.push(name, [annotation])

To fit AppleCake's workflow, annotations are treated the same as args in .profile. AppleCake does not cache the profile tables for you, so you may notice additional memory usage.

Parameter Optional Description
name Name of the profile, this is so you can identify the profile later in the viewer.
annotation Defaults to nil. Annotation is a value (string, number, table types). That can be later viewed in the profiling data.
Example

.jprof.pop([name])

Pop the current profile from the stack. Acts the same as jprof as if you provide it with a name, it will check the name against the current head of the stack.

Parameter Optional Description
name Name of the profile, so that you know you're popping the correct profile.
Example

.jprof.popAll()

Pops all the current profile from the stack. Acts as expected; the same as jprof.

Example

.jprof.write([filename])

This function acts a little differently due to appleCake's workflow of always having the file open. This function will close the current open file (if open) and then open the new file with the given filename. If no filename is given, it will default to profile.json

Parameter Optional Description
filename Name of the file to write to open and write to.
Example

.jprof.enabled(enabled)

Enables or disables profiling after this function call.

Parameter Optional Description
enabled Boolean to enable or disable profiling.
Example

.jprof.connect() / .jprog.netFlush()

These functions are not supported at all within AppleCake and will throw an error if used.

.jprof.START([filename, name])

Same function as .beginSession. For your conveniences it was added to the jprof table.

.jprof.COUNTMEMORY([memoryScale])

Same function as .countMemory. For your conveniences it was added to the jprof table.