<commit_before>/*
 * Copyright (C) 2019 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.gapid.perfetto.models;

import static com.google.gapid.perfetto.views.StyleConstants.DEFAULT_COUNTER_TRACK_HEIGHT;
import static com.google.gapid.perfetto.views.StyleConstants.PROCESS_COUNTER_TRACK_HIGHT;
import static com.google.gapid.perfetto.views.TrackContainer.group;
import static com.google.gapid.perfetto.views.TrackContainer.single;
import static java.util.stream.Collectors.toList;

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gapid.models.Perfetto;
import com.google.gapid.perfetto.canvas.Panel;
import com.google.gapid.perfetto.models.TrackConfig.Group;
import com.google.gapid.perfetto.views.CounterPanel;
import com.google.gapid.perfetto.views.CpuFrequencyPanel;
import com.google.gapid.perfetto.views.CpuPanel;
import com.google.gapid.perfetto.views.CpuSummaryPanel;
import com.google.gapid.perfetto.views.FrameEventsSummaryPanel;
import com.google.gapid.perfetto.views.GpuQueuePanel;
import com.google.gapid.perfetto.views.ProcessSummaryPanel;
import com.google.gapid.perfetto.views.ThreadPanel;
import com.google.gapid.perfetto.views.TitlePanel;
import com.google.gapid.perfetto.views.VulkanCounterPanel;
import com.google.gapid.util.Scheduler;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Determines what tracks to show for a trace.
 */
public class Tracks {
  // CPU usage percentage at which a process/thread is considered idle.
  private static final double IDLE_PERCENT_CUTOFF = 0.001; // 0.1%.

  private Tracks() {
  }

  public static ListenableFuture<Perfetto.Data.Builder> enumerate(Perfetto.Data.Builder data) {
    return Scheduler.EXECUTOR.submit(() -> {
      enumerateCpu(data);
      enumerateCounters(data);
      enumerateGpu(data);
      enumerateProcesses(data);
      enumerateVSync(data);
      return data;
    });
  }

  private static Perfetto.Data.Builder enumerateCpu(Perfetto.Data.Builder data) {
    if (!data.getCpu().hasCpus()) {
      return data;
    }

    CpuSummaryTrack summary = new CpuSummaryTrack(data.qe, data.getCpu().count());
    boolean hasAnyFrequency = false;
    for (CpuInfo.Cpu cpu : data.getCpu().cpus()) {
      CpuTrack track = new CpuTrack(data.qe, cpu);
      data.tracks.addTrack(summary.getId(), track.getId(), "CPU " + cpu.id,
          single(state -> new CpuPanel(state, track), false));
      if (cpu.hasFrequency()) {
        CpuFrequencyTrack freqTrack = new CpuFrequencyTrack(data.qe, cpu);
        data.tracks.addTrack(summary.getId(), freqTrack.getId(), "CPU " + cpu.id + " Frequency",
            single(state -> new CpuFrequencyPanel(state, freqTrack), false));
        hasAnyFrequency = true;
      }
    }

    Group.UiFactory ui = hasAnyFrequency ?
        group(state -> new CpuSummaryPanel(state, summary), true, (group, filtered) -> {
          for (int cpu = 0, track = 0; cpu < data.getCpu().count(); cpu++, track++) {
            if (data.getCpu().get(cpu).hasFrequency()) {
              track++;
              group.setVisible(track, !filtered);
            }
          }
        }, true) :
        group(state -> new CpuSummaryPanel(state, summary), true);
    data.tracks.addLabelGroup(null, summary.getId(), "CPU Usage", ui);
    return data;
  }

  public static Perfetto.Data.Builder enumerateCounters(Perfetto.Data.Builder data) {
    MemorySummaryTrack.enumerate(data);
    BatterySummaryTrack.enumerate(data);
    return data;
  }

  public static Perfetto.Data.Builder enumerateGpu(Perfetto.Data.Builder data) {
    List<CounterInfo> counters = data.getCounters(CounterInfo.Type.Gpu).values().stream()
        .filter(c -> c.count > 0)
        .collect(toList());

    if (counters.isEmpty() && data.getGpu().isEmpty()) {
      // No GPU data available.
      return data;
    }

    data.tracks.addLabelGroup(null, "gpu", "GPU", group(state -> new TitlePanel("GPU"), true));

    if (data.getGpu().queueCount() > 0) {
      String parent = "gpu";
      if (data.getGpu().queueCount() > 1) {
        data.tracks.addLabelGroup(
            "gpu", "gpu_queues", "GPU Queues", group(state -> new TitlePanel("GPU Queues"), true));
        parent = "gpu_queues";
      }
      for (GpuInfo.Queue queue : data.getGpu().queues()) {
        SliceTrack track = SliceTrack.forGpuQueue(data.qe, queue);
        data.tracks.addTrack(parent, track.getId(), queue.getDisplay(),
            single(state -> new GpuQueuePanel(state, queue, track), true));
      }
    }

    if (data.getGpu().bufferCount() > 0) {
      String parent = "gpu";
      if (data.getGpu().bufferCount() > 1) {
        data.tracks.addLabelGroup("gpu", "sf_events", "Surface Flinger Events",
            group(state -> new TitlePanel("Surface Flinger Events"), true));
        parent = "sf_events";
      }
      for (GpuInfo.Buffer buffer : data.getGpu().buffers()) {
        FrameEventsTrack track = FrameEventsTrack.forBuffer(data.qe, buffer);
        data.tracks.addTrack(parent, track.getId(), buffer.getDisplay(),
            single(state -> new FrameEventsSummaryPanel(state, buffer, track), true));
      }
    }

    if (!counters.isEmpty()) {
      data.tracks.addLabelGroup("gpu", "gpu_counters", "GPU Counters",
          group(state -> new TitlePanel("GPU Counters"), true));
      for (CounterInfo counter : counters) {
        CounterTrack track = new CounterTrack(data.qe, counter);
        data.tracks.addTrack("gpu_counters", track.getId(), counter.name,
            single(state -> new CounterPanel(state, track, DEFAULT_COUNTER_TRACK_HEIGHT), true));
      }
    }
    return data;
  }

  public static Perfetto.Data.Builder enumerateProcesses(Perfetto.Data.Builder data) {
    List<ProcessInfo> processes = Lists.newArrayList(data.getProcesses().values());
    Collections.sort(processes, (p1, p2) -> Long.compare(p2.totalDur, p1.totalDur));
    final int count = processes.size();
    if (count == 0) {
      return data;
    }<commit_msg>Only show the "GPU Counters" group if there's more than one counter.<commit_after>/*
 * Copyright (C) 2019 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.gapid.perfetto.models;

import static com.google.gapid.perfetto.views.StyleConstants.DEFAULT_COUNTER_TRACK_HEIGHT;
import static com.google.gapid.perfetto.views.StyleConstants.PROCESS_COUNTER_TRACK_HIGHT;
import static com.google.gapid.perfetto.views.TrackContainer.group;
import static com.google.gapid.perfetto.views.TrackContainer.single;
import static java.util.stream.Collectors.toList;

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gapid.models.Perfetto;
import com.google.gapid.perfetto.canvas.Panel;
import com.google.gapid.perfetto.models.TrackConfig.Group;
import com.google.gapid.perfetto.views.CounterPanel;
import com.google.gapid.perfetto.views.CpuFrequencyPanel;
import com.google.gapid.perfetto.views.CpuPanel;
import com.google.gapid.perfetto.views.CpuSummaryPanel;
import com.google.gapid.perfetto.views.FrameEventsSummaryPanel;
import com.google.gapid.perfetto.views.GpuQueuePanel;
import com.google.gapid.perfetto.views.ProcessSummaryPanel;
import com.google.gapid.perfetto.views.ThreadPanel;
import com.google.gapid.perfetto.views.TitlePanel;
import com.google.gapid.perfetto.views.VulkanCounterPanel;
import com.google.gapid.util.Scheduler;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Determines what tracks to show for a trace.
 */
public class Tracks {
  // CPU usage percentage at which a process/thread is considered idle.
  private static final double IDLE_PERCENT_CUTOFF = 0.001; // 0.1%.

  private Tracks() {
  }

  public static ListenableFuture<Perfetto.Data.Builder> enumerate(Perfetto.Data.Builder data) {
    return Scheduler.EXECUTOR.submit(() -> {
      enumerateCpu(data);
      enumerateCounters(data);
      enumerateGpu(data);
      enumerateProcesses(data);
      enumerateVSync(data);
      return data;
    });
  }

  private static Perfetto.Data.Builder enumerateCpu(Perfetto.Data.Builder data) {
    if (!data.getCpu().hasCpus()) {
      return data;
    }

    CpuSummaryTrack summary = new CpuSummaryTrack(data.qe, data.getCpu().count());
    boolean hasAnyFrequency = false;
    for (CpuInfo.Cpu cpu : data.getCpu().cpus()) {
      CpuTrack track = new CpuTrack(data.qe, cpu);
      data.tracks.addTrack(summary.getId(), track.getId(), "CPU " + cpu.id,
          single(state -> new CpuPanel(state, track), false));
      if (cpu.hasFrequency()) {
        CpuFrequencyTrack freqTrack = new CpuFrequencyTrack(data.qe, cpu);
        data.tracks.addTrack(summary.getId(), freqTrack.getId(), "CPU " + cpu.id + " Frequency",
            single(state -> new CpuFrequencyPanel(state, freqTrack), false));
        hasAnyFrequency = true;
      }
    }

    Group.UiFactory ui = hasAnyFrequency ?
        group(state -> new CpuSummaryPanel(state, summary), true, (group, filtered) -> {
          for (int cpu = 0, track = 0; cpu < data.getCpu().count(); cpu++, track++) {
            if (data.getCpu().get(cpu).hasFrequency()) {
              track++;
              group.setVisible(track, !filtered);
            }
          }
        }, true) :
        group(state -> new CpuSummaryPanel(state, summary), true);
    data.tracks.addLabelGroup(null, summary.getId(), "CPU Usage", ui);
    return data;
  }

  public static Perfetto.Data.Builder enumerateCounters(Perfetto.Data.Builder data) {
    MemorySummaryTrack.enumerate(data);
    BatterySummaryTrack.enumerate(data);
    return data;
  }

  public static Perfetto.Data.Builder enumerateGpu(Perfetto.Data.Builder data) {
    List<CounterInfo> counters = data.getCounters(CounterInfo.Type.Gpu).values().stream()
        .filter(c -> c.count > 0)
        .collect(toList());

    if (counters.isEmpty() && data.getGpu().isEmpty()) {
      // No GPU data available.
      return data;
    }

    data.tracks.addLabelGroup(null, "gpu", "GPU", group(state -> new TitlePanel("GPU"), true));

    if (data.getGpu().queueCount() > 0) {
      String parent = "gpu";
      if (data.getGpu().queueCount() > 1) {
        data.tracks.addLabelGroup(
            "gpu", "gpu_queues", "GPU Queues", group(state -> new TitlePanel("GPU Queues"), true));
        parent = "gpu_queues";
      }
      for (GpuInfo.Queue queue : data.getGpu().queues()) {
        SliceTrack track = SliceTrack.forGpuQueue(data.qe, queue);
        data.tracks.addTrack(parent, track.getId(), queue.getDisplay(),
            single(state -> new GpuQueuePanel(state, queue, track), true));
      }
    }

    if (data.getGpu().bufferCount() > 0) {
      String parent = "gpu";
      if (data.getGpu().bufferCount() > 1) {
        data.tracks.addLabelGroup("gpu", "sf_events", "Surface Flinger Events",
            group(state -> new TitlePanel("Surface Flinger Events"), true));
        parent = "sf_events";
      }
      for (GpuInfo.Buffer buffer : data.getGpu().buffers()) {
        FrameEventsTrack track = FrameEventsTrack.forBuffer(data.qe, buffer);
        data.tracks.addTrack(parent, track.getId(), buffer.getDisplay(),
            single(state -> new FrameEventsSummaryPanel(state, buffer, track), true));
      }
    }

    if (!counters.isEmpty()) {
      String parent = "gpu";
      if (counters.size() > 1) {
        data.tracks.addLabelGroup("gpu", "gpu_counters", "GPU Counters",
            group(state -> new TitlePanel("GPU Counters"), true));
        parent = "gpu_counters";
      }
      for (CounterInfo counter : counters) {
        CounterTrack track = new CounterTrack(data.qe, counter);
        data.tracks.addTrack(parent, track.getId(), counter.name,
            single(state -> new CounterPanel(state, track, DEFAULT_COUNTER_TRACK_HEIGHT), true));
      }
    }
    return data;
  }

  public static Perfetto.Data.Builder enumerateProcesses(Perfetto.Data.Builder data) {
    List<ProcessInfo> processes = Lists.newArrayList(data.getProcesses().values());
    Collections.sort(processes, (p1, p2) -> Long.compare(p2.totalDur, p1.totalDur));
    final int count = processes.size();
    if (count == 0) {
      return data;
    }
{'fraction_non_alphanumeric': 0.12961578711317087, 'fraction_numerical': 0.003961728210494842, 'mean_word_length': 3.3908762717426977, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 50, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '254817', 'n_tokens_mistral': 4211, 'n_tokens_neox': 4097, 'n_words': 907}