/****************************************************************************
 * apps/system/perf-tools/evlist.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you 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.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <nuttx/list.h>

#include "evlist.h"
#include "evsel.h"

/****************************************************************************
 * Private Functions
 ****************************************************************************/

static void perf_evlist_init(FAR struct perf_evlist_s *evlist)
{
  list_initialize(&evlist->entries);
  evlist->nr_entries = 0;
}

static void perf_evlist_add(FAR struct perf_evlist_s *evlist,
                            FAR struct perf_evsel_s *evsel)
{
  list_add_tail(&evlist->entries, &evsel->node);
  evlist->nr_entries += 1;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

void evlist_init(FAR struct evlist_s *evlist)
{
  perf_evlist_init(&evlist->core);
}

FAR struct evlist_s *evlist_new(void)
{
  FAR struct evlist_s *evlist = zalloc(sizeof(struct evlist_s));

  if (evlist != NULL)
    {
      evlist_init(evlist);
    }

  return evlist;
}

int evlist_delete(FAR struct evlist_s *evlist)
{
  struct perf_evsel_s *evsel;

  if (evlist == NULL)
    {
      return -EINVAL;
    }

  list_for_every_entry(&evlist->core.entries, evsel,
                       struct perf_evsel_s, node)
    {
      if (evsel->id)
        {
          free(evsel->id);
        }
    }

  evlist->core.nr_entries = 0;

  free(evlist->attrs);
  free(evlist);

  return 0;
}

void evlist_add(FAR struct evlist_s *evlist, FAR struct evsel_s *entry)
{
  perf_evlist_add(&evlist->core, &entry->core);
  entry->evlist = evlist;
}

int evlist_add_attrs(FAR struct evlist_s *evlist,
                     FAR struct perf_event_attr_s *attrs,
                     size_t nr_attrs, int cpu)
{
  FAR struct evsel_s *evsel;
  FAR struct list_node *node;
  size_t i;

  for (i = 0; i < nr_attrs; i++)
    {
      evsel = evsel_new(attrs + i);
      if (evsel == NULL)
        {
          goto out_delete;
        }

      evsel->core.cpu = cpu;
      evsel->core.pid = evlist->pid;
      evlist_add(evlist, evsel);
    }

  return 0;

out_delete:
  list_for_every(&(evlist->core.entries), node)
    {
      FAR struct perf_evsel_s *psel;
      psel = list_container_of(node, struct perf_evsel_s, node);
      evsel = list_container_of(psel, struct evsel_s, core);
      evsel_delete(evsel);
    }

  return -ENOMEM;
}

int evlist_print_counters(FAR struct evlist_s *evlist)
{
  FAR struct perf_evsel_s *psel = NULL;
  FAR struct evsel_s *evsel = NULL;
  FAR struct list_node *node = NULL;
#if (CONFIG_SMP_NCPUS > 1)
  struct evlist_count_s *evcnt = NULL;
  int i = 0;

  if (evlist->system_wide)
    {
      int num = evlist->core.nr_entries / CONFIG_SMP_NCPUS;
      evcnt = zalloc(num * sizeof(struct evlist_count_s));
      if (!evcnt)
        {
          return -ENOMEM;
        }

      list_for_every(&(evlist->core.entries), node)
        {
          psel = list_container_of(node, struct perf_evsel_s, node);
          evsel = list_container_of(psel, struct evsel_s, core);
          evcnt[i % num].count += evsel->core.count;
          if (i < num)
            {
              evcnt[i].name = evsel_name(evsel);
            }

          i++;
        }

      for (i = 0; i < num; i++)
        {
          printf("%10ld\t %s\n", evcnt[i].count, evcnt[i].name);
        }

      free(evcnt);
    }
  else
#endif
    {
      list_for_every(&(evlist->core.entries), node)
        {
          psel = list_container_of(node, struct perf_evsel_s, node);
          evsel = list_container_of(psel, struct evsel_s, core);
          printf("%10ld\t %s\n", evsel->core.count, evsel_name(evsel));
        }
    }

  return 0;
}