Problem
This expands on the idea originally put forth in:
taskcluster/taskcluster#7426
Currently, many tasks in Gecko have profiles that automatically get uploaded. You can even click a button in Treeherder to open the profiles in the Firefox Profiler. It's a great feature! The problem is that the thing responsible for the profile is mozsystemonitor in-tree. This means there are large swathes of the task that can't be profiled. E.g, anything that run-task does such as cloning the repo or downloading fetches. It also means it only for Gecko tasks.
We could find a way to run mozsystemmonitor in run-task, but that still has blind spots. Such as anything that generic-worker handles like fetching the docker image or setting up caches.
I'd like a way to get a profile of the entire task lifecycle. And I'd like it to be something that's available to all tasks in Firefox-CI. Regardless of which project they're in, and regardless of whether they use run-task or not.
This means we need to make profiling part of the platform.
Idea
The high level idea here is that generic-worker will provide a new feature that tasks can opt-in to, whereupon it'll enable sampling throughout the task's lifecycle, and emit a profile as an artifact upon task completion. The flow looks something like:
- Task has
features: {"profile": true} in the task definition
- generic-worker starts sampling resource usage using something like
gopsutil (CPU, memory, network IO etc)
- generic-worker starts the task and creates lifecycle markers for things like downloading docker images, or setting up caches. This way we can track how long each of these steps take
- When task is finished generic-worker outputs all profile data as an artifact
Custom markers
There's a big gap here though. Generic-worker has no visibility into the task payload, so it can't know e.g, how long a clone or a build took. For this it will need to delegate to the task itself. There are likely many ways to solve this, but one simple way would be for generic-worker to set a TASKCLUSTER_PROFILE_DIR variable. Then task processes can write their own markers in files like $TASKCLUSTER_PROFILE_DIR/<pid>.jsonl (one file per process to avoid racing).
Then, generic-worker simply has to merge in the JSON objects in these files to the final profile. In this way, run-task can emit data about how long a clone took. Or build scripts can emit data on how long various stages of the build were. The final profile will contain both the internal markers, and custom markers. As well as counters for resource usage (so the final profile can map various segments of the task to usage).
Profile Format
The format of the profiles should follow the Firefox Profiler format. While it has Firefox in the name, the format is actually agnostic to Mozilla. It's used by other browser vendors, and other companies that have nothing to do with browsers. It's well documented and several implementations exist (though unfortunately none in Go yet).
Yes, we partly pick this format because it's already what Mozilla uses.. But it's also a fantastic tool used across many companies and open source projects. So it makes a lot of sense for this use case.
Future Worker Profiling
In the future, it might be nice to be able to profile the entire worker lifecycle even between tasks. I think that should be out-of-scope for now, but I could imagine a daemon that runs on the workers sampling everything. If it's present, generic-worker would know to how to interface with it such that it can retrieve samples from it and include them in the per-task profiles it generates. We could emit per worker profiles somehow, e.g if we wanted to better track idle time between tasks.
Maybe it would make sense to go this route even if we don't do full worker sampling, and that way generic-worker could delegate resource usage monitoring to a different tool?
I'm interested in people's thoughts! If reception to the overall idea is generally positive, I can follow-up with a proper RFC that goes into more specifics on the implementation.
Problem
This expands on the idea originally put forth in:
taskcluster/taskcluster#7426
Currently, many tasks in Gecko have profiles that automatically get uploaded. You can even click a button in Treeherder to open the profiles in the Firefox Profiler. It's a great feature! The problem is that the thing responsible for the profile is mozsystemonitor in-tree. This means there are large swathes of the task that can't be profiled. E.g, anything that
run-taskdoes such as cloning the repo or downloading fetches. It also means it only for Gecko tasks.We could find a way to run
mozsystemmonitorinrun-task, but that still has blind spots. Such as anything thatgeneric-workerhandles like fetching the docker image or setting up caches.I'd like a way to get a profile of the entire task lifecycle. And I'd like it to be something that's available to all tasks in Firefox-CI. Regardless of which project they're in, and regardless of whether they use
run-taskor not.This means we need to make profiling part of the platform.
Idea
The high level idea here is that
generic-workerwill provide a new feature that tasks can opt-in to, whereupon it'll enable sampling throughout the task's lifecycle, and emit a profile as an artifact upon task completion. The flow looks something like:features: {"profile": true}in the task definitiongopsutil(CPU, memory, network IO etc)Custom markers
There's a big gap here though. Generic-worker has no visibility into the task payload, so it can't know e.g, how long a clone or a build took. For this it will need to delegate to the task itself. There are likely many ways to solve this, but one simple way would be for generic-worker to set a
TASKCLUSTER_PROFILE_DIRvariable. Then task processes can write their own markers in files like$TASKCLUSTER_PROFILE_DIR/<pid>.jsonl(one file per process to avoid racing).Then, generic-worker simply has to merge in the JSON objects in these files to the final profile. In this way,
run-taskcan emit data about how long a clone took. Or build scripts can emit data on how long various stages of the build were. The final profile will contain both the internal markers, and custom markers. As well as counters for resource usage (so the final profile can map various segments of the task to usage).Profile Format
The format of the profiles should follow the Firefox Profiler format. While it has Firefox in the name, the format is actually agnostic to Mozilla. It's used by other browser vendors, and other companies that have nothing to do with browsers. It's well documented and several implementations exist (though unfortunately none in Go yet).
Yes, we partly pick this format because it's already what Mozilla uses.. But it's also a fantastic tool used across many companies and open source projects. So it makes a lot of sense for this use case.
Future Worker Profiling
In the future, it might be nice to be able to profile the entire worker lifecycle even between tasks. I think that should be out-of-scope for now, but I could imagine a daemon that runs on the workers sampling everything. If it's present, generic-worker would know to how to interface with it such that it can retrieve samples from it and include them in the per-task profiles it generates. We could emit per worker profiles somehow, e.g if we wanted to better track idle time between tasks.
Maybe it would make sense to go this route even if we don't do full worker sampling, and that way generic-worker could delegate resource usage monitoring to a different tool?
I'm interested in people's thoughts! If reception to the overall idea is generally positive, I can follow-up with a proper RFC that goes into more specifics on the implementation.