xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.ProcessSummary/index.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2021 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {Trace} from '../../public/trace';
16import {PerfettoPlugin} from '../../public/plugin';
17import {getThreadOrProcUri} from '../../public/utils';
18import {NUM, NUM_NULL, STR} from '../../trace_processor/query_result';
19import {
20  Config as ProcessSchedulingTrackConfig,
21  PROCESS_SCHEDULING_TRACK_KIND,
22  ProcessSchedulingTrack,
23} from './process_scheduling_track';
24import {
25  Config as ProcessSummaryTrackConfig,
26  PROCESS_SUMMARY_TRACK,
27  ProcessSummaryTrack,
28} from './process_summary_track';
29import ThreadPlugin from '../dev.perfetto.Thread';
30
31// This plugin is responsible for adding summary tracks for process and thread
32// groups.
33export default class implements PerfettoPlugin {
34  static readonly id = 'dev.perfetto.ProcessSummary';
35  static readonly dependencies = [ThreadPlugin];
36
37  async onTraceLoad(ctx: Trace): Promise<void> {
38    await this.addProcessTrackGroups(ctx);
39    await this.addKernelThreadSummary(ctx);
40  }
41
42  private async addProcessTrackGroups(ctx: Trace): Promise<void> {
43    const threads = ctx.plugins.getPlugin(ThreadPlugin).getThreadMap();
44
45    const cpuCount = Math.max(...ctx.traceInfo.cpus, -1) + 1;
46
47    const result = await ctx.engine.query(`
48      INCLUDE PERFETTO MODULE android.process_metadata;
49
50      select *
51      from (
52        select
53          _process_available_info_summary.upid,
54          null as utid,
55          process.pid,
56          null as tid,
57          process.name as processName,
58          null as threadName,
59          sum_running_dur > 0 as hasSched,
60          android_process_metadata.debuggable as isDebuggable,
61          ifnull((
62            select group_concat(string_value)
63            from args
64            where
65              process.arg_set_id is not null and
66              arg_set_id = process.arg_set_id and
67              flat_key = 'chrome.process_label'
68          ), '') as chromeProcessLabels
69        from _process_available_info_summary
70        join process using(upid)
71        left join android_process_metadata using(upid)
72      )
73      union all
74      select *
75      from (
76        select
77          null,
78          utid,
79          null as pid,
80          tid,
81          null as processName,
82          thread.name threadName,
83          sum_running_dur > 0 as hasSched,
84          0 as isDebuggable,
85          '' as chromeProcessLabels
86        from _thread_available_info_summary
87        join thread using (utid)
88        where upid is null
89      )
90  `);
91
92    const it = result.iter({
93      upid: NUM_NULL,
94      utid: NUM_NULL,
95      pid: NUM_NULL,
96      tid: NUM_NULL,
97      hasSched: NUM_NULL,
98      isDebuggable: NUM_NULL,
99      chromeProcessLabels: STR,
100    });
101    for (; it.valid(); it.next()) {
102      const upid = it.upid;
103      const utid = it.utid;
104      const pid = it.pid;
105      const tid = it.tid;
106      const hasSched = Boolean(it.hasSched);
107      const isDebuggable = Boolean(it.isDebuggable);
108      const subtitle = it.chromeProcessLabels;
109
110      // Group by upid if present else by utid.
111      const pidForColor = pid ?? tid ?? upid ?? utid ?? 0;
112      const uri = getThreadOrProcUri(upid, utid);
113
114      const chips: string[] = [];
115      isDebuggable && chips.push('debuggable');
116
117      if (hasSched) {
118        const config: ProcessSchedulingTrackConfig = {
119          pidForColor,
120          upid,
121          utid,
122        };
123
124        ctx.tracks.registerTrack({
125          uri,
126          title: `${upid === null ? tid : pid} schedule`,
127          tags: {
128            kind: PROCESS_SCHEDULING_TRACK_KIND,
129          },
130          chips,
131          track: new ProcessSchedulingTrack(ctx, config, cpuCount, threads),
132          subtitle,
133        });
134      } else {
135        const config: ProcessSummaryTrackConfig = {
136          pidForColor,
137          upid,
138          utid,
139        };
140
141        ctx.tracks.registerTrack({
142          uri,
143          title: `${upid === null ? tid : pid} summary`,
144          tags: {
145            kind: PROCESS_SUMMARY_TRACK,
146          },
147          chips,
148          track: new ProcessSummaryTrack(ctx.engine, config),
149          subtitle,
150        });
151      }
152    }
153  }
154
155  private async addKernelThreadSummary(ctx: Trace): Promise<void> {
156    const {engine} = ctx;
157
158    // Identify kernel threads if this is a linux system trace, and sufficient
159    // process information is available. Kernel threads are identified by being
160    // children of kthreadd (always pid 2).
161    // The query will return the kthreadd process row first, which must exist
162    // for any other kthreads to be returned by the query.
163    // TODO(rsavitski): figure out how to handle the idle process (swapper),
164    // which has pid 0 but appears as a distinct process (with its own comm) on
165    // each cpu. It'd make sense to exclude its thread state track, but still
166    // put process-scoped tracks in this group.
167    const result = await engine.query(`
168      select
169        t.utid, p.upid, (case p.pid when 2 then 1 else 0 end) isKthreadd
170      from
171        thread t
172        join process p using (upid)
173        left join process parent on (p.parent_upid = parent.upid)
174        join
175          (select true from metadata m
176             where (m.name = 'system_name' and m.str_value = 'Linux')
177           union
178           select 1 from (select true from sched limit 1))
179      where
180        p.pid = 2 or parent.pid = 2
181      order by isKthreadd desc
182    `);
183
184    const it = result.iter({
185      utid: NUM,
186      upid: NUM,
187    });
188
189    // Not applying kernel thread grouping.
190    if (!it.valid()) {
191      return;
192    }
193
194    const config: ProcessSummaryTrackConfig = {
195      pidForColor: 2,
196      upid: it.upid,
197      utid: it.utid,
198    };
199
200    ctx.tracks.registerTrack({
201      uri: '/kernel',
202      title: `Kernel thread summary`,
203      tags: {
204        kind: PROCESS_SUMMARY_TRACK,
205      },
206      track: new ProcessSummaryTrack(ctx.engine, config),
207    });
208  }
209}
210