/* cross-thread-frees.c
 *
 * Copyright 2020 Christian Hergert <chergert@redhat.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#include "config.h"

#include <errno.h>
#include <glib.h>
#include <stddef.h>
#include <rax.h>
#include <sysprof.h>

typedef struct
{
  gint tid;
  guint n_addrs;
  gint64 size;
  SysprofCaptureAddress addrs[0];
} Stack;

static void
stack_free (gpointer ptr)
{
  Stack *stack = ptr;
  gsize size = sizeof *stack + (stack->n_addrs * sizeof (SysprofCaptureAddress));
  g_slice_free1 (size, stack);
}

static Stack *
stack_new (gint                         tid,
           gint64                       size,
           guint                        n_addrs,
           const SysprofCaptureAddress *addrs)
{
  Stack *stack;

  stack = g_slice_alloc (sizeof *stack + (n_addrs * sizeof (SysprofCaptureAddress)));
  stack->tid = tid;
  stack->size = size;
  stack->n_addrs = n_addrs;
  for (guint i = 0; i < n_addrs; i++)
    stack->addrs[i] = addrs[i];

  return stack;
}

static void
cross_thread_frees (SysprofCaptureReader *reader)
{
  SysprofCaptureFrameType type;
  g_autoptr(GHashTable) stacks = NULL;

  stacks = g_hash_table_new_full (NULL, NULL, NULL, stack_free);

  while (sysprof_capture_reader_peek_type (reader, &type))
    {
      if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION)
        {
          const SysprofCaptureAllocation *ev = sysprof_capture_reader_read_allocation (reader);
          gpointer key;

          if (ev == NULL)
            break;

          key = GINT_TO_POINTER (ev->alloc_addr);

          if (ev->alloc_size > 0)
            {
              g_hash_table_insert (stacks,
                                   key,
                                   stack_new (ev->tid, ev->alloc_size, ev->n_addrs, ev->addrs));
            }
          else
            {
              Stack *stack;

              stack = g_hash_table_lookup (stacks, key);
              if (stack == NULL)
                continue;

              if (ev->tid != stack->tid)
                {
                  g_print ("Alloc-Thread=%d Free-Thread=%d Size=%"G_GUINT64_FORMAT"\n",
                           stack->tid, ev->tid, stack->size);
                }

              g_hash_table_remove (stacks, key);
            }
        }
      else
        {
          if (!sysprof_capture_reader_skip (reader))
            break;
        }
    }
}

gint
main (gint   argc,
      gchar *argv[])
{
  SysprofCaptureReader *reader;
  const gchar *filename = argv[1];

  if (argc < 2)
    {
      g_printerr ("usage: %s FILENAME\n", argv[0]);
      return EXIT_FAILURE;
    }

  if (!(reader = sysprof_capture_reader_new (filename)))
    {
      int errsv = errno;
      g_printerr ("%s\n", g_strerror (errsv));
      return EXIT_FAILURE;
    }

  cross_thread_frees (reader);

  sysprof_capture_reader_unref (reader);

  return EXIT_SUCCESS;
}
