Skip to content

Add profile feature to generic-worker #197

Description

@ahal

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:

  1. Task has features: {"profile": true} in the task definition
  2. generic-worker starts sampling resource usage using something like gopsutil (CPU, memory, network IO etc)
  3. 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
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions