build: remove libsysprof

This library is going away now that we have -analyze and -profile libs.
This commit is contained in:
Christian Hergert
2023-07-17 11:38:25 -07:00
parent eb72b1100b
commit 12e75e4c8c
108 changed files with 2 additions and 23092 deletions

View File

@ -259,8 +259,7 @@ int main(void) {
config_h.set10('HAVE_STDATOMIC_H', true)
endif
needs_service_access = get_option('libsysprof') or get_option('agent')
install_service_files = needs_service_access or get_option('sysprofd') == 'bundled'
install_service_files = get_option('sysprofd') == 'bundled'
if need_glib
subdir('contrib')

View File

@ -8,9 +8,6 @@ option('development', type: 'boolean', value: 'false')
# server scenarios.
option('gtk', type: 'boolean')
# Disable libsysprof/ui (in situations you only want sysprof-capture)
option('libsysprof', type: 'boolean')
# Allow disabling the installation of libsysprof-capture*.a
option('install-static', type: 'boolean')
@ -29,12 +26,6 @@ option('systemdunitdir', type: 'string',
description: 'Directory for systemd service files'
)
# An optional location to specify where to locate debug information. This
# is useful for distributions to set based on their debuginfo setup.
option('debugdir', type: 'string',
description: 'Look for global separate debug info in this path'
)
# If Yelp documentation should be installed
option('help', type: 'boolean')

View File

@ -20,7 +20,7 @@
#include "config.h"
#include "../libsysprof/elfparser.h"
#include "elfparser.h"
#include "sysprof-elf-private.h"

View File

@ -1,576 +0,0 @@
/* MemProf -- memory profiler and leak detector
* Copyright 1999, 2000, 2001, Red Hat, Inc.
* Copyright 2002, Kristian Rietveld
*
* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, 2005, 2006, 2007, Soeren Sandmann
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* Most interesting code in this file is lifted from bfdutils.c
* and process.c from Memprof,
*/
#include "config.h"
#include <glib.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include "binfile.h"
#include "elfparser.h"
struct bin_file_t
{
int ref_count;
GList * elf_files;
char * filename;
char * undefined_name;
gulong text_offset;
gboolean inode_check;
ino_t inode;
};
static ino_t
read_inode (const char *filename)
{
struct stat statbuf;
if (strcmp (filename, "[vdso]") == 0)
return (ino_t)0;
if (stat (filename, &statbuf) < 0)
return (ino_t)-1;
return statbuf.st_ino;
}
static gboolean
already_warned (const char *name)
{
static GPtrArray *warnings;
guint i;
if (!warnings)
warnings = g_ptr_array_new ();
for (i = 0; i < warnings->len; ++i)
{
if (strcmp (warnings->pdata[i], name) == 0)
return TRUE;
}
g_ptr_array_add (warnings, g_strdup (name));
return FALSE;
}
static ElfParser *
get_build_id_file (ElfParser *elf,
const char * const *debug_dirs)
{
const char *build_id;
GList *tries = NULL, *list;
char *init, *rest;
ElfParser *result = NULL;
char *tmp;
build_id = elf_parser_get_build_id (elf);
if (!build_id)
return NULL;
if (strlen (build_id) < 4)
return NULL;
init = g_strndup (build_id, 2);
rest = g_strdup_printf ("%s%s", build_id + 2, ".debug");
if (debug_dirs)
{
for (guint i = 0; debug_dirs[i]; i++)
{
tmp = g_build_filename (debug_dirs[i], ".build-id", init, rest, NULL);
tries = g_list_append (tries, tmp);
}
}
for (list = tries; list != NULL; list = list->next)
{
char *name = list->data;
ElfParser *parser = elf_parser_new (name, NULL);
if (parser)
{
const char *file_id = elf_parser_get_build_id (parser);
if (file_id && strcmp (build_id, file_id) == 0)
{
result = parser;
break;
}
elf_parser_free (parser);
}
}
g_list_foreach (tries, (GFunc)g_free, NULL);
g_list_free (tries);
g_free (init);
g_free (rest);
return result;
}
static ElfParser *
get_debuglink_file (ElfParser *elf,
const char *filename,
char **new_name,
const gchar * const *debug_dirs)
{
const char *basename;
guint32 crc32;
ElfParser *result = NULL;
const char *build_id;
char *dir;
const char *files;
const char *prefix = "";
if (!elf)
return NULL;
basename = elf_parser_get_debug_link (elf, &crc32);
build_id = elf_parser_get_build_id (elf);
#if 0
g_print (" debug link for %s is %s\n", filename, basename);
#endif
if (!basename)
return NULL;
dir = g_path_get_dirname (filename);
/* Flatpak paths have the form of ".../files/bin" or ".../files/lib/x86_64-linux-gnu". */
files = g_strrstr (dir, "/files/");
if (files)
prefix = files + sizeof ("/files/") - 1;
for (guint i = 0; debug_dirs[i]; i++)
{
/* Most files from Flatpak will be from .Platform, which usually has a prefix like "lib/x86_64-linux-gnu"
* but in the debug dir the files are under "usr/lib/x86_64-linux-gnu", so try with "usr" first. */
g_autofree char *name = g_build_filename (debug_dirs[i], "usr", prefix, basename, NULL);
ElfParser *parser = elf_parser_new (name, NULL);
guint32 file_crc;
const char *file_build_id;
if (!parser)
{
/* Files from Flatpak com.example.App.Debug have prefixes like "bin" or "lib",
* and they don't need the "usr" for the debug dir, so try without "usr". */
g_free (name);
name = g_build_filename (debug_dirs[i], prefix, basename, NULL);
parser = elf_parser_new (name, NULL);
}
if (parser)
{
/* If both files have build ids, and they don't match,
* there is no point computing a CRC32 that we know
* will fail
*/
file_build_id = elf_parser_get_build_id (parser);
if (build_id && file_build_id && strcmp (build_id, file_build_id) != 0)
goto skip;
file_crc = elf_parser_get_crc32 (parser);
if (file_crc == crc32)
{
result = parser;
*new_name = g_steal_pointer (&name);
break;
}
else
{
if (!already_warned (name))
{
g_print ("warning: %s has wrong crc %x, %s has crc %x)\n",
name, file_crc, filename, crc32);
}
}
skip:
elf_parser_free (parser);
}
}
g_free (dir);
return result;
}
static GList *
get_debug_binaries (GList *files,
ElfParser *elf,
const char *filename,
const gchar * const *debug_dirs)
{
ElfParser *build_id_file;
GHashTable *seen_names;
GList *free_us = NULL;
build_id_file = get_build_id_file (elf, debug_dirs);
if (build_id_file)
return g_list_prepend (files, build_id_file);
/* .gnu_debuglink is actually a chain of debuglinks, and
* there have been real-world cases where following it was
* necessary to get useful debug information.
*/
seen_names = g_hash_table_new (g_str_hash, g_str_equal);
while (elf)
{
char *debug_name;
if (g_hash_table_lookup (seen_names, filename))
break;
g_hash_table_insert (seen_names, (char *)filename, (char *)filename);
elf = get_debuglink_file (elf, filename, &debug_name, debug_dirs);
if (elf)
{
files = g_list_prepend (files, elf);
free_us = g_list_prepend (free_us, debug_name);
filename = debug_name;
}
}
g_list_foreach (free_us, (GFunc)g_free, NULL);
g_list_free (free_us);
g_hash_table_destroy (seen_names);
return files;
}
#ifdef __linux__
G_GNUC_PRINTF (1, 2)
static char **
get_lines (const char *format,
...)
{
va_list args;
char *filename;
char **result = NULL;
char *contents;
va_start (args, format);
filename = g_strdup_vprintf (format, args);
va_end (args);
if (g_file_get_contents (filename, &contents, NULL, NULL))
{
result = g_strsplit (contents, "\n", -1);
g_free (contents);
}
g_free (filename);
return result;
}
#endif
static const uint8_t *
get_vdso_bytes (size_t *length)
{
#ifdef __linux__
static const uint8_t *bytes = NULL;
static size_t n_bytes = 0;
static gboolean has_data;
if (!has_data)
{
char **lines = get_lines ("/proc/%d/maps", getpid());
int i;
for (i = 0; lines[i] != NULL; ++i)
{
char file[256];
gulong start;
gulong end;
int count = sscanf (
lines[i], "%lx-%lx %*15s %*x %*x:%*x %*u %255s",
&start, &end, file);
if (count == 3 && strcmp (file, "[vdso]") == 0)
{
n_bytes = end - start;
/* Dup the memory here so that valgrind will only
* report one 1 byte invalid read instead of
* a ton when the elf parser scans the vdso
*
* The reason we get a spurious invalid read from
* valgrind is that we are getting the address directly
* from /proc/maps, and valgrind knows that its mmap()
* wrapper never returned that address. But since it
* is a legal mapping, it is legal to read it.
*/
bytes = g_memdup2 ((uint8_t *)start, n_bytes);
has_data = TRUE;
}
}
}
if (length)
*length = n_bytes;
return bytes;
#else
if (length)
*length = 0;
return NULL;
#endif
}
bin_file_t *
bin_file_new (const char *filename,
const gchar * const *debug_dirs)
{
const gchar *real_filename = filename;
ElfParser *elf = NULL;
bin_file_t *bf;
bf = g_new0 (bin_file_t, 1);
bf->inode_check = FALSE;
bf->filename = g_strdup (filename);
bf->undefined_name = g_strdup_printf ("In file %s", real_filename);
bf->ref_count = 1;
bf->elf_files = NULL;
if (strcmp (filename, "[vdso]") == 0)
{
const guint8 *vdso_bytes;
gsize length;
vdso_bytes = get_vdso_bytes (&length);
if (vdso_bytes)
elf = elf_parser_new_from_data (vdso_bytes, length);
}
else
{
elf = elf_parser_new (filename, NULL);
}
if (elf)
{
/* We need the text offset of the actual binary, not the
* (potential) debug binaries
*/
bf->text_offset = elf_parser_get_text_offset (elf);
bf->elf_files = get_debug_binaries (bf->elf_files, elf, filename, debug_dirs);
bf->elf_files = g_list_append (bf->elf_files, elf);
bf->inode = read_inode (filename);
}
return bf;
}
void
bin_file_free (bin_file_t *bin_file)
{
if (--bin_file->ref_count == 0)
{
g_list_foreach (bin_file->elf_files, (GFunc)elf_parser_free, NULL);
g_list_free (bin_file->elf_files);
g_free (bin_file->filename);
g_free (bin_file->undefined_name);
g_free (bin_file);
}
}
const bin_symbol_t *
bin_file_lookup_symbol (bin_file_t *bin_file,
gulong address)
{
GList *list;
#if 0
g_print ("-=-=-=- \n");
g_print ("bin file lookup lookup %d\n", address);
#endif
address -= bin_file->text_offset;
#if 0
g_print ("lookup %lx in %s\n", address, bin_file->filename);
#endif
for (list = bin_file->elf_files; list != NULL; list = list->next)
{
ElfParser *elf = list->data;
const ElfSym *sym = elf_parser_lookup_symbol (elf, address);
if (sym)
{
#if 0
g_print ("found %lx => %s\n", address,
bin_symbol_get_name (bin_file, (const bin_symbol_t *)sym));
#endif
return (const bin_symbol_t *)sym;
}
}
#if 0
g_print ("%lx undefined in %s (textoffset %lx)\n",
address + bin_file->text_offset,
bin_file->filename,
bin_file->text_offset);
#endif
return (const bin_symbol_t *)bin_file->undefined_name;
}
gboolean
bin_file_check_inode (bin_file_t *bin_file,
ino_t inode)
{
if (bin_file->inode == inode)
return TRUE;
if (!bin_file->elf_files)
return FALSE;
if (!bin_file->inode_check)
{
g_print ("warning: Inode mismatch for %s (disk: %"G_GUINT64_FORMAT", memory: %"G_GUINT64_FORMAT")\n",
bin_file->filename, (guint64)bin_file->inode, (guint64)inode);
bin_file->inode_check = TRUE;
}
return FALSE;
}
static const ElfSym *
get_elf_sym (bin_file_t *file,
const bin_symbol_t *symbol,
ElfParser **elf_ret)
{
GList *list;
for (list = file->elf_files; list != NULL; list = list->next)
{
const ElfSym *sym = (const ElfSym *)symbol;
ElfParser *elf = list->data;
if (elf_parser_owns_symbol (elf, sym))
{
*elf_ret = elf;
return sym;
}
}
g_critical ("Internal error: unrecognized symbol pointer");
*elf_ret = NULL;
return NULL;
}
const char *
bin_symbol_get_name (bin_file_t *file,
const bin_symbol_t *symbol)
{
if (file->undefined_name == (char *)symbol)
{
return file->undefined_name;
}
else
{
ElfParser *elf;
const ElfSym *sym;
sym = get_elf_sym (file, symbol, &elf);
return elf_parser_get_sym_name (elf, sym);
}
}
gulong
bin_symbol_get_address (bin_file_t *file,
const bin_symbol_t *symbol)
{
if (file->undefined_name == (char *)symbol)
{
return 0x0;
}
else
{
ElfParser *elf;
const ElfSym *sym;
sym = get_elf_sym (file, symbol, &elf);
return elf_parser_get_sym_address (elf, sym);
}
}
void
bin_symbol_get_address_range (bin_file_t *file,
const bin_symbol_t *symbol,
gulong *begin,
gulong *end)
{
if (file->undefined_name == (char *)symbol)
{
*begin = 0;
*end = 0;
}
else
{
ElfParser *elf;
const ElfSym *sym;
sym = get_elf_sym (file, symbol, &elf);
elf_parser_get_sym_address_range (elf, sym, begin, end);
}
}

View File

@ -1,51 +0,0 @@
/* MemProf -- memory profiler and leak detector
* Copyright 1999, 2000, 2001, Red Hat, Inc.
* Copyright 2002, Kristian Rietveld
*
* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* Copyright 2004, 2005, Soeren Sandmann
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef BIN_FILE_H
#define BIN_FILE_H
#include <glib.h>
#include <sys/types.h>
typedef struct bin_file_t bin_file_t;
typedef struct bin_symbol_t bin_symbol_t;
/* Binary File */
bin_file_t *bin_file_new (const char *filename,
const gchar * const *debug_dirs);
void bin_file_free (bin_file_t *bin_file);
const bin_symbol_t *bin_file_lookup_symbol (bin_file_t *bin_file,
gulong address);
gboolean bin_file_check_inode (bin_file_t *bin_file,
ino_t inode);
const char *bin_symbol_get_name (bin_file_t *bin_file,
const bin_symbol_t *symbol);
gulong bin_symbol_get_address (bin_file_t *bin_file,
const bin_symbol_t *symbol);
void bin_symbol_get_address_range (bin_file_t *bin_file,
const bin_symbol_t *symbol,
gulong *begin,
gulong *end);
#endif

View File

@ -1,40 +0,0 @@
/* demangle.cpp
*
* Copyright (C) 2016 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/>.
*/
#include <cxxabi.h>
#include <stdlib.h>
#include "demangle.h"
gchar *
sysprof_cplus_demangle (const gchar *name)
{
char *real_name;
gchar *ret;
int status;
real_name = abi::__cxa_demangle (name, 0, 0, &status);
if (real_name == NULL)
return NULL;
ret = g_strdup (real_name);
free (real_name);
return ret;
}

View File

@ -1,28 +0,0 @@
/* demangle.h
*
* Copyright 2016-2019 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/>.
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
G_GNUC_INTERNAL
gchar *sysprof_cplus_demangle (const gchar *name);
G_END_DECLS

View File

@ -1,903 +0,0 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2006, 2007, Soeren Sandmann
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <string.h>
#ifdef __APPLE__
# include <libelf.h>
#else
# include <elf.h>
#endif
#include <sys/mman.h>
#include "demangle.h"
#include "elfparser.h"
typedef struct Section Section;
struct ElfSym
{
gulong table;
gulong offset;
gulong address;
};
struct Section
{
const gchar * name;
gsize offset;
gsize size;
gboolean allocated;
gulong load_address;
guint type;
};
struct ElfParser
{
gboolean is_64;
const guchar * data;
gsize length;
guint n_sections;
Section ** sections;
guint n_symbols;
ElfSym * symbols;
gsize sym_strings;
GMappedFile * file;
char * filename;
gboolean checked_build_id;
char * build_id;
const Section * text_section;
};
/* FIXME: All of these should in principle do endian swapping,
* but sysprof never has to deal with binaries of a different
* endianness than sysprof itself
*/
#define GET_FIELD(parser, offset, struct_name, idx, field_name) \
(((parser))->is_64? \
((Elf64_ ## struct_name *)(gpointer)(((parser)->data + offset)) + (idx))->field_name : \
((Elf32_ ## struct_name *)(gpointer)(((parser)->data + offset)) + (idx))->field_name)
#define GET_UINT32(parser, offset) \
*((uint32_t *)(gpointer)(parser->data + offset)) \
#define GET_SIZE(parser, struct_name) \
(((parser)->is_64? \
sizeof (Elf64_ ## struct_name) : \
sizeof (Elf32_ ## struct_name)))
#define MAKE_ELF_UINT_ACCESSOR(field_name) \
static uint64_t field_name (ElfParser *parser) \
{ \
return GET_FIELD (parser, 0, Ehdr, 0, field_name); \
}
MAKE_ELF_UINT_ACCESSOR (e_shoff)
MAKE_ELF_UINT_ACCESSOR (e_shnum)
MAKE_ELF_UINT_ACCESSOR (e_shstrndx)
#define MAKE_SECTION_HEADER_ACCESSOR(field_name) \
static uint64_t field_name (ElfParser *parser, int nth_section) \
{ \
gsize offset = e_shoff (parser); \
\
return GET_FIELD (parser, offset, Shdr, nth_section, field_name); \
}
MAKE_SECTION_HEADER_ACCESSOR (sh_name);
MAKE_SECTION_HEADER_ACCESSOR (sh_type);
MAKE_SECTION_HEADER_ACCESSOR (sh_flags);
MAKE_SECTION_HEADER_ACCESSOR (sh_addr);
MAKE_SECTION_HEADER_ACCESSOR (sh_offset);
MAKE_SECTION_HEADER_ACCESSOR (sh_size);
#define MAKE_SYMBOL_ACCESSOR(field_name) \
static uint64_t field_name (ElfParser *parser, gulong offset, gulong nth) \
{ \
return GET_FIELD (parser, offset, Sym, nth, field_name); \
}
MAKE_SYMBOL_ACCESSOR(st_name);
MAKE_SYMBOL_ACCESSOR(st_info);
MAKE_SYMBOL_ACCESSOR(st_value);
MAKE_SYMBOL_ACCESSOR(st_size);
MAKE_SYMBOL_ACCESSOR(st_shndx);
static gboolean
in_container (void)
{
static gboolean _in_container;
static gboolean initialized;
if (!initialized)
{
/* Flatpak has /.flatpak-info
* Podman has /run/.containerenv
*
* Both have access to host files via /var/run/host.
*/
_in_container = g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS) ||
g_file_test ("/run/.containerenv", G_FILE_TEST_EXISTS);
initialized = TRUE;
}
return _in_container;
}
static void
section_free (Section *section)
{
g_free (section);
}
static const Section *
find_section (ElfParser *parser,
const char *name,
guint type)
{
guint i;
for (i = 0; i < parser->n_sections; ++i)
{
Section *section = parser->sections[i];
if (strcmp (section->name, name) == 0 && section->type == type)
return section;
}
return NULL;
}
static gboolean
parse_elf_signature (const guchar *data,
gsize length,
gboolean *is_64,
gboolean *is_be)
{
/* FIXME: this function should be able to return an error */
if (length < EI_NIDENT)
{
/* FIXME set error */
return FALSE;
}
if (data[EI_CLASS] != ELFCLASS32 &&
data[EI_CLASS] != ELFCLASS64)
{
/* FIXME set error */
return FALSE;
}
if (data[EI_DATA] != ELFDATA2LSB &&
data[EI_DATA] != ELFDATA2MSB)
{
/* FIXME set error */
return FALSE;
}
if (is_64)
*is_64 = (data[EI_CLASS] == ELFCLASS64);
if (is_be)
*is_be = (data[EI_DATA] == ELFDATA2MSB);
return TRUE;
}
ElfParser *
elf_parser_new_from_data (const guchar *data,
gsize length)
{
ElfParser *parser;
gboolean is_64, is_big_endian;
int section_names_idx;
const guchar *section_names;
G_GNUC_UNUSED gsize section_headers;
guint i;
if (!parse_elf_signature (data, length, &is_64, &is_big_endian))
{
/* FIXME: set error */
return NULL;
}
parser = g_new0 (ElfParser, 1);
parser->is_64 = is_64;
parser->data = data;
parser->length = length;
#if 0
g_print (" new parser : %p\n", parser);
#endif
/* Read ELF header */
parser->n_sections = e_shnum (parser);
section_names_idx = e_shstrndx (parser);
section_headers = e_shoff (parser);
/* Read section headers */
parser->sections = g_new0 (Section *, parser->n_sections);
section_names = parser->data + sh_offset (parser, section_names_idx);
for (i = 0; i < parser->n_sections; ++i)
{
Section *section = g_new (Section, 1);
section->name = (char *)(section_names + sh_name (parser, i));
section->size = sh_size (parser, i);
section->offset = sh_offset (parser, i);
section->allocated = !!(sh_flags (parser, i) & SHF_ALLOC);
if (section->allocated)
section->load_address = sh_addr (parser, i);
else
section->load_address = 0;
section->type = sh_type (parser, i);
parser->sections[i] = section;
}
/* Cache the text section */
parser->text_section = find_section (parser, ".text", SHT_PROGBITS);
if (!parser->text_section)
parser->text_section = find_section (parser, ".text", SHT_NOBITS);
parser->filename = NULL;
parser->build_id = NULL;
return parser;
}
static GMappedFile *
open_mapped_file (const char *filename,
GError **error)
{
GMappedFile *file = NULL;
char *alternate = NULL;
if (in_container () && !g_str_has_prefix (filename, g_get_home_dir ()))
{
alternate = g_build_filename ("/var/run/host", filename, NULL);
file = g_mapped_file_new (alternate, FALSE, NULL);
g_free (alternate);
}
/* Flatpaks with filesystem=host don't have Silverblue's /sysroot in /var/run/host,
* yet they have it in /, which means the original path might work.
*/
if (!file)
file = g_mapped_file_new (filename, FALSE, error);
return file;
}
ElfParser *
elf_parser_new_from_mmap (GMappedFile *file,
GError **error)
{
const guchar *data;
gsize length;
ElfParser *parser;
if (file == NULL)
return NULL;
data = (guchar *)g_mapped_file_get_contents (file);
length = g_mapped_file_get_length (file);
parser = elf_parser_new_from_data (data, length);
if (!parser)
{
g_set_error (error,
G_FILE_ERROR,
G_FILE_ERROR_FAILED,
"Failed to load ELF from mmap region");
g_mapped_file_unref (file);
return NULL;
}
parser->filename = NULL;
parser->file = file;
return parser;
}
ElfParser *
elf_parser_new (const char *filename,
GError **error)
{
GMappedFile *file;
const guchar *data;
gsize length;
ElfParser *parser;
if (!(file = open_mapped_file (filename, error)))
return NULL;
#if 0
g_print ("elf parser new : %s\n", filename);
#endif
data = (guchar *)g_mapped_file_get_contents (file);
length = g_mapped_file_get_length (file);
#if 0
g_print ("data %p: for %s\n", data, filename);
#endif
parser = elf_parser_new_from_data (data, length);
#if 0
g_print ("Parser for %s: %p\n", filename, parser);
#endif
if (!parser)
{
g_set_error (error,
G_FILE_ERROR,
G_FILE_ERROR_FAILED,
"Failed to load ELF from file %s",
filename);
g_mapped_file_unref (file);
return NULL;
}
parser->filename = g_strdup (filename);
parser->file = file;
#if 0
g_print ("Elf file: %s (debug: %s)\n",
filename, elf_parser_get_debug_link (parser, NULL));
if (!parser->symbols)
g_print ("at this point %s has no symbols\n", filename);
#endif
return parser;
}
guint32
elf_parser_get_crc32 (ElfParser *parser)
{
static const unsigned long crc32_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
const guchar *data;
gsize length;
gulong crc;
gsize i;
data = parser->data;
length = parser->length;
crc = 0xffffffff;
madvise ((char *)data, length, MADV_SEQUENTIAL);
for (i = 0; i < length; ++i)
crc = crc32_table[(crc ^ data[i]) & 0xff] ^ (crc >> 8);
/* We just read the entire file into memory, but we only really
* need the symbol table, so swap the whole thing out.
*
* We could be more exact here, but it's only a few minor
* pagefaults.
*/
if (parser->file)
madvise ((char *)data, length, MADV_DONTNEED);
return ~crc & 0xffffffff;
}
void
elf_parser_free (ElfParser *parser)
{
guint i;
for (i = 0; i < parser->n_sections; ++i)
section_free (parser->sections[i]);
g_free (parser->sections);
if (parser->file)
g_mapped_file_unref (parser->file);
g_free (parser->symbols);
if (parser->filename)
g_free (parser->filename);
if (parser->build_id)
g_free (parser->build_id);
g_free (parser);
}
gchar *
elf_demangle (const char *name)
{
gchar *demangled = sysprof_cplus_demangle (name);
if (demangled)
return demangled;
else
return g_strdup (name);
}
/*
* Looking up symbols
*/
static int
compare_sym (const void *a, const void *b)
{
const ElfSym *sym_a = a;
const ElfSym *sym_b = b;
if (sym_a->address < sym_b->address)
return -1;
else if (sym_a->address == sym_b->address)
return 0;
else
return 1;
}
#if 0
static void
dump_symbols (ElfParser *parser, ElfSym *syms, guint n_syms)
{
int i;
for (i = 0; i < n_syms; ++i)
{
ElfSym *s = &(syms[i]);
g_print (" %s: %lx\n", elf_parser_get_sym_name (parser, s), s->address);
}
}
#endif
static void
read_table (ElfParser *parser,
const Section *sym_table,
const Section *str_table)
{
int sym_size = GET_SIZE (parser, Sym);
guint i, n_symbols;
#if 0
g_print ("elf: Reading table for %s\n", parser->filename? parser->filename : "<unknown>");
#endif
parser->n_symbols = sym_table->size / sym_size;
parser->symbols = g_new (ElfSym, parser->n_symbols);
#if 0
g_print ("sym table offset: %d\n", sym_table->offset);
#endif
n_symbols = 0;
#if 0
g_print ("n syms: %d\n", parser->n_symbols);
#endif
for (i = 0; i < parser->n_symbols; ++i)
{
guint info;
gulong addr;
gulong shndx;
info = st_info (parser, sym_table->offset, i);
addr = st_value (parser, sym_table->offset, i);
shndx = st_shndx (parser, sym_table->offset, i);
#if 0
g_print ("read symbol: %s (section: %d)\n", get_string_indirct (parser->parser,
parser->sym_format, "st_name",
str_table->offset),
shndx);
#endif
if (addr != 0 &&
shndx < parser->n_sections &&
parser->sections[shndx] == parser->text_section &&
(info & 0xf) == STT_FUNC &&
((info >> 4) == STB_GLOBAL ||
(info >> 4) == STB_LOCAL ||
(info >> 4) == STB_WEAK))
{
parser->symbols[n_symbols].address = addr;
parser->symbols[n_symbols].table = sym_table->offset;
parser->symbols[n_symbols].offset = i;
n_symbols++;
#if 0
g_print (" symbol: %s: %lx\n",
get_string_indirect (parser->parser,
parser->sym_format, "st_name",
str_table->offset),
addr - parser->text_section->load_address);
g_print (" sym %d in %p (info: %d:%d) (func:global %d:%d)\n",
addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL);
#endif
}
else if (addr != 0)
{
#if 0
g_print (" rejecting %d in %p (info: %d:%d) (func:global %d:%d)\n",
addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL);
#endif
}
}
parser->sym_strings = str_table->offset;
parser->n_symbols = n_symbols;
/* Allocate space for at least one symbol, so that parser->symbols will be
* non-NULL. If it ends up being NULL, we will be parsing the file over and
* over.
*/
parser->symbols = g_renew (ElfSym, parser->symbols, parser->n_symbols + 1);
qsort (parser->symbols, parser->n_symbols, sizeof (ElfSym), compare_sym);
}
static void
read_symbols (ElfParser *parser)
{
const Section *symtab = find_section (parser, ".symtab", SHT_SYMTAB);
const Section *strtab = find_section (parser, ".strtab", SHT_STRTAB);
const Section *dynsym = find_section (parser, ".dynsym", SHT_DYNSYM);
const Section *dynstr = find_section (parser, ".dynstr", SHT_STRTAB);
if (symtab && strtab)
{
#if 0
g_print ("reading symbol table of %s\n", parser->filename);
#endif
read_table (parser, symtab, strtab);
}
else if (dynsym && dynstr)
{
#if 0
g_print ("reading dynamic symbol table of %s\n", parser->filename);
#endif
read_table (parser, dynsym, dynstr);
}
else
{
/* To make sure parser->symbols is non-NULL */
parser->n_symbols = 0;
parser->symbols = g_new (ElfSym, 1);
}
}
static ElfSym *
do_lookup (ElfSym *symbols,
gulong address,
int first,
int last)
{
if (address >= symbols[last].address)
{
return &(symbols[last]);
}
else if (last - first < 3)
{
while (last >= first)
{
if (address >= symbols[last].address)
return &(symbols[last]);
last--;
}
return NULL;
}
else
{
int mid = (first + last) / 2;
if (symbols[mid].address > address)
return do_lookup (symbols, address, first, mid);
else
return do_lookup (symbols, address, mid, last);
}
}
/* Address should be given in 'offset into text segment' */
const ElfSym *
elf_parser_lookup_symbol (ElfParser *parser,
gulong address)
{
const ElfSym *result;
if (!parser->symbols)
{
#if 0
g_print ("reading symbols at %p\n", parser);
#endif
read_symbols (parser);
}
if (parser->n_symbols == 0)
return NULL;
if (!parser->text_section)
return NULL;
address += parser->text_section->load_address;
#if 0
g_print ("elf: the address we are looking up is %p\n", address);
#endif
result = do_lookup (parser->symbols, address, 0, parser->n_symbols - 1);
#if 0
if (result)
{
g_print (" elf: found %s at %lx\n", elf_parser_get_sym_name (parser, result), result->address);
}
else
{
g_print ("elf: not found\n");
}
#endif
if (result)
{
gulong size = st_size (parser, result->table, result->offset);
if (size > 0 && result->address + size <= address)
{
#if 0
g_print (" elf: ends at %lx, so rejecting\n",
result->address + size);
#endif
result = NULL;
}
}
if (result)
{
/* Reject the symbols if the address is outside the text section */
if (address > parser->text_section->load_address + parser->text_section->size)
result = NULL;
}
return result;
}
gulong
elf_parser_get_text_offset (ElfParser *parser)
{
g_return_val_if_fail (parser != NULL, (gulong)-1);
if (!parser->text_section)
return (gulong)-1;
return parser->text_section->offset;
}
static gchar *
make_hex_string (const guchar *data, int n_bytes)
{
static const char hex_digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
GString *string = g_string_new (NULL);
int i;
for (i = 0; i < n_bytes; ++i)
{
char c = data[i];
g_string_append_c (string, hex_digits[(c & 0xf0) >> 4]);
g_string_append_c (string, hex_digits[(c & 0x0f)]);
}
return g_string_free (string, FALSE);
}
const gchar *
elf_parser_get_build_id (ElfParser *parser)
{
if (!parser->checked_build_id)
{
const Section *build_id =
find_section (parser, ".note.gnu.build-id", SHT_NOTE);
guint64 name_size;
guint64 desc_size;
guint64 type;
const char *name;
guint64 offset;
parser->checked_build_id = TRUE;
if (!build_id)
return NULL;
offset = build_id->offset;
name_size = GET_FIELD (parser, offset, Nhdr, 0, n_namesz);
desc_size = GET_FIELD (parser, offset, Nhdr, 0, n_descsz);
type = GET_FIELD (parser, offset, Nhdr, 0, n_type);
offset += GET_SIZE (parser, Nhdr);
name = (char *)(parser->data + offset);
if (strncmp (name, ELF_NOTE_GNU, name_size) != 0 || type != NT_GNU_BUILD_ID)
return NULL;
offset += strlen (name);
offset = (offset + 3) & (~0x3);
parser->build_id = make_hex_string (parser->data + offset, desc_size);
}
return parser->build_id;
}
const char *
elf_parser_get_debug_link (ElfParser *parser,
guint32 *crc32)
{
guint64 offset;
const Section *debug_link = find_section (parser, ".gnu_debuglink",
SHT_PROGBITS);
const gchar *result;
if (!debug_link)
return NULL;
offset = debug_link->offset;
result = (char *)(parser->data + offset);
if (crc32)
{
int len = strlen (result) + 1;
offset = (offset + len + 3) & ~0x3;
*crc32 = GET_UINT32 (parser, offset);
}
return result;
}
static const guchar *
get_section (ElfParser *parser,
const char *name)
{
const Section *section = find_section (parser, name, SHT_PROGBITS);
if (section)
return parser->data + section->offset;
else
return NULL;
}
const guchar *
elf_parser_get_eh_frame (ElfParser *parser)
{
return get_section (parser, ".eh_frame");
}
const guchar *
elf_parser_get_debug_frame (ElfParser *parser)
{
return get_section (parser, ".debug_frame");
}
const char *
elf_parser_get_sym_name (ElfParser *parser,
const ElfSym *sym)
{
g_return_val_if_fail (parser != NULL, NULL);
return (char *)(parser->data + parser->sym_strings +
st_name (parser, sym->table, sym->offset));
}
gboolean
elf_parser_owns_symbol (ElfParser *parser,
const ElfSym *sym)
{
ElfSym *first, *last;
if (!parser->n_symbols)
return FALSE;
first = parser->symbols;
last = parser->symbols + parser->n_symbols - 1;
return first <= sym && sym <= last;
}
gulong
elf_parser_get_sym_address (ElfParser *parser,
const ElfSym *sym)
{
return sym->address - parser->text_section->load_address;
}
void
elf_parser_get_sym_address_range (ElfParser *parser,
const ElfSym *sym,
gulong *begin,
gulong *end)
{
*begin = sym->address - parser->text_section->load_address;
*end = *begin + st_size (parser, sym->table, sym->offset);
}

View File

@ -1,68 +0,0 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2006, 2007, Soeren Sandmann
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <glib.h>
#ifndef NT_GNU_BUILD_ID
#define NT_GNU_BUILD_ID 3
#endif
typedef struct ElfSym ElfSym;
typedef struct ElfParser ElfParser;
ElfParser *elf_parser_new_from_data (const guchar *data,
gsize length);
ElfParser *elf_parser_new_from_mmap (GMappedFile *mapped_file,
GError **err);
ElfParser *elf_parser_new (const char *filename,
GError **err);
void elf_parser_free (ElfParser *parser);
const char *elf_parser_get_debug_link (ElfParser *parser,
guint32 *crc32);
const gchar *elf_parser_get_build_id (ElfParser *parser);
const guchar *elf_parser_get_eh_frame (ElfParser *parser);
const guchar *elf_parser_get_debug_frame (ElfParser *parser);
gulong elf_parser_get_text_offset (ElfParser *parser);
/* Lookup a symbol in the file.
*
* The symbol returned is const, so don't free it. It is valid until
* elf_parser_free() is called on the parser.
*
* The address should be given in "file coordinates". This means that
* if the file is mapped at address m and offset o, then an address a
* should be looked up as "a - (m - o)". (m - o) is where the start
* of the file would have been mapped, so a - (m - o) is the position
* in the file of a.
*/
const ElfSym *elf_parser_lookup_symbol (ElfParser *parser,
gulong address);
guint32 elf_parser_get_crc32 (ElfParser *parser);
const char *elf_parser_get_sym_name (ElfParser *parser,
const ElfSym *sym);
gulong elf_parser_get_sym_address (ElfParser *parser,
const ElfSym *sym);
gboolean elf_parser_owns_symbol (ElfParser *parser,
const ElfSym *sym);
char *elf_demangle (const char *name);
void elf_parser_get_sym_address_range (ElfParser *parser,
const ElfSym *sym,
gulong *begin,
gulong *end);

View File

@ -1,121 +0,0 @@
/* mapped-ring-buffer-source.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
*/
#define G_LOG_DOMAIN "sysprof-mapped-ring-buffer-source"
#include "config.h"
#include <glib.h>
#include "mapped-ring-buffer-source.h"
typedef struct _MappedRingSource
{
GSource source;
MappedRingBuffer *buffer;
} MappedRingSource;
static gboolean
mapped_ring_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
MappedRingSource *real_source = (MappedRingSource *)source;
g_assert (source != NULL);
return mapped_ring_buffer_drain (real_source->buffer,
(MappedRingBufferCallback)callback,
user_data);
}
static void
mapped_ring_source_finalize (GSource *source)
{
MappedRingSource *real_source = (MappedRingSource *)source;
if (real_source != NULL)
g_clear_pointer (&real_source->buffer, mapped_ring_buffer_unref);
}
static gboolean
mapped_ring_source_check (GSource *source)
{
MappedRingSource *real_source = (MappedRingSource *)source;
g_assert (real_source != NULL);
g_assert (real_source->buffer != NULL);
return !mapped_ring_buffer_is_empty (real_source->buffer);
}
static gboolean
mapped_ring_source_prepare (GSource *source,
gint *timeout_)
{
MappedRingSource *real_source = (MappedRingSource *)source;
g_assert (real_source != NULL);
g_assert (real_source->buffer != NULL);
if (!mapped_ring_buffer_is_empty (real_source->buffer))
return TRUE;
*timeout_ = 5;
return FALSE;
}
static GSourceFuncs mapped_ring_source_funcs = {
.prepare = mapped_ring_source_prepare,
.check = mapped_ring_source_check,
.dispatch = mapped_ring_source_dispatch,
.finalize = mapped_ring_source_finalize,
};
guint
mapped_ring_buffer_create_source_full (MappedRingBuffer *self,
MappedRingBufferCallback source_func,
gpointer user_data,
GDestroyNotify destroy)
{
MappedRingSource *source;
guint ret;
g_return_val_if_fail (self != NULL, 0);
g_return_val_if_fail (source_func != NULL, 0);
source = (MappedRingSource *)g_source_new (&mapped_ring_source_funcs, sizeof (MappedRingSource));
source->buffer = mapped_ring_buffer_ref (self);
g_source_set_callback ((GSource *)source, (GSourceFunc)source_func, user_data, destroy);
g_source_set_name ((GSource *)source, "MappedRingSource");
ret = g_source_attach ((GSource *)source, g_main_context_default ());
g_source_unref ((GSource *)source);
return ret;
}
guint
mapped_ring_buffer_create_source (MappedRingBuffer *self,
MappedRingBufferCallback source_func,
gpointer user_data)
{
return mapped_ring_buffer_create_source_full (self, source_func, user_data, NULL);
}

View File

@ -1,47 +0,0 @@
/* mapped-ring-buffer-source.h
*
* 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include <glib.h>
#include "sysprof-version-macros.h"
#include "mapped-ring-buffer.h"
G_BEGIN_DECLS
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MappedRingBuffer, mapped_ring_buffer_unref)
G_GNUC_INTERNAL
guint mapped_ring_buffer_create_source (MappedRingBuffer *self,
MappedRingBufferCallback callback,
gpointer user_data);
G_GNUC_INTERNAL
guint mapped_ring_buffer_create_source_full (MappedRingBuffer *self,
MappedRingBufferCallback callback,
gpointer user_data,
GDestroyNotify destroy);
G_END_DECLS

View File

@ -1,198 +0,0 @@
libsysprof_c_args = [ '-DSYSPROF_COMPILATION' ]
libsysprof_public_sources = [
'sysprof-battery-source.c',
'sysprof-callgraph-profile.c',
'sysprof-capture-gobject.c',
'sysprof-capture-symbol-resolver.c',
'sysprof-control-source.c',
'sysprof-diskstat-source.c',
'sysprof-elf-symbol-resolver.c',
'sysprof-gjs-source.c',
'sysprof-governor-source.c',
'sysprof-hostinfo-source.c',
'sysprof-jitmap-symbol-resolver.c',
'sysprof-kernel-symbol.c',
'sysprof-kernel-symbol-resolver.c',
'sysprof-local-profiler.c',
'sysprof-memprof-profile.c',
'sysprof-memprof-source.c',
'sysprof-netdev-source.c',
'sysprof-preload-source.c',
'sysprof-process-model.c',
'sysprof-process-model-item.c',
'sysprof-profile.c',
'sysprof-profiler.c',
'sysprof-proxy-source.c',
'sysprof-selection.c',
'sysprof-source.c',
'sysprof-spawnable.c',
'sysprof-symbol-resolver.c',
'sysprof-symbols-source.c',
'sysprof-tracefd-source.c',
]
libsysprof_public_headers = [
'sysprof-battery-source.h',
'sysprof-callgraph-profile.h',
'sysprof-capture-autocleanups.h',
'sysprof-capture-gobject.h',
'sysprof-capture-symbol-resolver.h',
'sysprof-control-source.h',
'sysprof-diskstat-source.h',
'sysprof-elf-symbol-resolver.h',
'sysprof-gjs-source.h',
'sysprof-governor-source.h',
'sysprof-hostinfo-source.h',
'sysprof-jitmap-symbol-resolver.h',
'sysprof-netdev-source.h',
'sysprof-kernel-symbol.h',
'sysprof-kernel-symbol-resolver.h',
'sysprof-local-profiler.h',
'sysprof-memprof-profile.h',
'sysprof-memprof-source.h',
'sysprof-preload-source.h',
'sysprof-process-model.h',
'sysprof-process-model-item.h',
'sysprof-profile.h',
'sysprof-profiler.h',
'sysprof-proxy-source.h',
'sysprof-selection.h',
'sysprof-source.h',
'sysprof-spawnable.h',
'sysprof-symbol-resolver.h',
'sysprof-symbols-source.h',
'sysprof-tracefd-source.h',
'sysprof.h',
]
libsysprof_private_sources = [
'binfile.c',
'demangle.cpp',
'elfparser.c',
'mapped-ring-buffer-source.c',
'sysprof-flatpak.c',
'sysprof-helpers.c',
'sysprof-kallsyms.c',
'sysprof-line-reader.c',
'sysprof-map-lookaside.c',
'sysprof-mountinfo.c',
'sysprof-path-resolver.c',
'sysprof-podman.c',
'sysprof-polkit.c',
'sysprof-symbol-map.c',
ipc_service_src,
stackstash_sources,
helpers_sources,
]
libsysprof_public_sources += libsysprof_capture_sources
librax = static_library('rax', ['rax.c'],
c_args: [ '-Wno-declaration-after-statement',
'-Wno-format-nonliteral',
'-Wno-shadow' ],
gnu_symbol_visibility: 'hidden',
)
librax_dep = declare_dependency(
link_whole: librax,
include_directories: include_directories('.'),
)
polkit_dep = dependency('polkit-gobject-1', version: polkit_req_version, required: false)
if polkit_dep.found()
libsysprof_c_args += ['-DHAVE_POLKIT']
endif
if dependency('polkit-gobject-1', version: '>= 0.114', required: false).found()
libsysprof_c_args += ['-DHAVE_POLKIT_AUTOPTR']
endif
# Subset of dependencies used in generating the pkg-config file
libsysprof_pkg_deps = [
dependency('gio-2.0', version: glib_req_version),
dependency('gio-unix-2.0', version: glib_req_version),
dependency('json-glib-1.0'),
polkit_dep,
libsysprof_capture_deps,
]
if host_machine.system() == 'linux'
libsysprof_public_sources += [
'sysprof-memory-source.c',
'sysprof-perf-counter.c',
'sysprof-perf-source.c',
'sysprof-proc-source.c',
]
libsysprof_public_headers += [
'sysprof-memory-source.h',
'sysprof-perf-counter.h',
'sysprof-perf-source.h',
'sysprof-proc-source.h',
]
endif
if host_machine.system() == 'darwin'
libsysprof_pkg_deps += [ dependency('libelf') ]
libsysprof_c_args += [ '-DNT_GNU_BUILD_ID=3', '-DELF_NOTE_GNU="GNU"', '-D__LIBELF_INTERNAL__' ]
endif
libsysprof_deps = libsysprof_pkg_deps
libsysprof_libs_private = []
if host_machine.system() != 'darwin'
libsysprof_deps += [cxx.find_library('stdc++')]
libsysprof_libs_private += '-lstdc++'
endif
libsysprof_static = static_library(
'sysprof',
(libsysprof_public_sources +
libsysprof_private_sources +
mapped_ring_buffer_sources),
include_directories: [include_directories('.'),
ipc_include_dirs,
libsysprof_capture_include_dirs],
dependencies: libsysprof_deps,
c_args: libsysprof_c_args,
gnu_symbol_visibility: 'hidden',
)
libsysprof_static_dep = declare_dependency(
link_whole: libsysprof_static,
dependencies: libsysprof_deps + [librax_dep],
include_directories: [include_directories('.'), libsysprof_capture_include_dirs],
)
if get_option('libsysprof')
libsysprof = shared_library('sysprof-@0@'.format(libsysprof_api_version),
dependencies: libsysprof_deps + [libsysprof_static_dep],
install: true,
install_dir: get_option('libdir'),
)
libsysprof_dep = declare_dependency(
link_with: libsysprof,
dependencies: libsysprof_deps,
include_directories: [include_directories('.'), libsysprof_capture_include_dirs],
)
meson.override_dependency('sysprof-@0@'.format(libsysprof_api_version), libsysprof_dep)
pkgconfig.generate(
libsysprof,
subdirs: [ sysprof_header_subdir ],
description: 'The library for console applications embedding sysprof',
install_dir: join_paths(get_option('libdir'), 'pkgconfig'),
requires: [ 'gio-2.0' ],
libraries_private: [libsysprof_libs_private, libsysprof_pkg_deps],
variables: [
'datadir=' + datadir_for_pc_file,
],
)
install_headers(libsysprof_public_headers, subdir: sysprof_header_subdir)
endif

File diff suppressed because it is too large Load Diff

View File

@ -1,216 +0,0 @@
/* Rax -- A radix tree implementation.
*
* Copyright (c) 2017-2018, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RAX_H
#define RAX_H
#include <stdint.h>
/* Representation of a radix tree as implemented in this file, that contains
* the strings "foo", "foobar" and "footer" after the insertion of each
* word. When the node represents a key inside the radix tree, we write it
* between [], otherwise it is written between ().
*
* This is the vanilla representation:
*
* (f) ""
* \
* (o) "f"
* \
* (o) "fo"
* \
* [t b] "foo"
* / \
* "foot" (e) (a) "foob"
* / \
* "foote" (r) (r) "fooba"
* / \
* "footer" [] [] "foobar"
*
* However, this implementation implements a very common optimization where
* successive nodes having a single child are "compressed" into the node
* itself as a string of characters, each representing a next-level child,
* and only the link to the node representing the last character node is
* provided inside the representation. So the above representation is turned
* into:
*
* ["foo"] ""
* |
* [t b] "foo"
* / \
* "foot" ("er") ("ar") "foob"
* / \
* "footer" [] [] "foobar"
*
* However this optimization makes the implementation a bit more complex.
* For instance if a key "first" is added in the above radix tree, a
* "node splitting" operation is needed, since the "foo" prefix is no longer
* composed of nodes having a single child one after the other. This is the
* above tree and the resulting node splitting after this event happens:
*
*
* (f) ""
* /
* (i o) "f"
* / \
* "firs" ("rst") (o) "fo"
* / \
* "first" [] [t b] "foo"
* / \
* "foot" ("er") ("ar") "foob"
* / \
* "footer" [] [] "foobar"
*
* Similarly after deletion, if a new chain of nodes having a single child
* is created (the chain must also not include nodes that represent keys),
* it must be compressed back into a single node.
*
*/
#define RAX_NODE_MAX_SIZE ((1<<29)-1)
typedef struct raxNode {
uint32_t iskey:1; /* Does this node contain a key? */
uint32_t isnull:1; /* Associated value is NULL (don't store it). */
uint32_t iscompr:1; /* Node is compressed. */
uint32_t size:29; /* Number of children, or compressed string len. */
/* Data layout is as follows:
*
* If node is not compressed we have 'size' bytes, one for each children
* character, and 'size' raxNode pointers, point to each child node.
* Note how the character is not stored in the children but in the
* edge of the parents:
*
* [header iscompr=0][abc][a-ptr][b-ptr][c-ptr](value-ptr?)
*
* if node is compressed (iscompr bit is 1) the node has 1 children.
* In that case the 'size' bytes of the string stored immediately at
* the start of the data section, represent a sequence of successive
* nodes linked one after the other, for which only the last one in
* the sequence is actually represented as a node, and pointed to by
* the current compressed node.
*
* [header iscompr=1][xyz][z-ptr](value-ptr?)
*
* Both compressed and not compressed nodes can represent a key
* with associated data in the radix tree at any level (not just terminal
* nodes).
*
* If the node has an associated key (iskey=1) and is not NULL
* (isnull=0), then after the raxNode pointers pointing to the
* children, an additional value pointer is present (as you can see
* in the representation above as "value-ptr" field).
*/
unsigned char data[];
} raxNode;
typedef struct rax {
raxNode *head;
uint64_t numele;
uint64_t numnodes;
} rax;
/* Stack data structure used by raxLowWalk() in order to, optionally, return
* a list of parent nodes to the caller. The nodes do not have a "parent"
* field for space concerns, so we use the auxiliary stack when needed. */
#define RAX_STACK_STATIC_ITEMS 32
typedef struct raxStack {
void **stack; /* Points to static_items or an heap allocated array. */
size_t items, maxitems; /* Number of items contained and total space. */
/* Up to RAXSTACK_STACK_ITEMS items we avoid to allocate on the heap
* and use this static array of pointers instead. */
void *static_items[RAX_STACK_STATIC_ITEMS];
int oom; /* True if pushing into this stack failed for OOM at some point. */
} raxStack;
/* Optional callback used for iterators and be notified on each rax node,
* including nodes not representing keys. If the callback returns true
* the callback changed the node pointer in the iterator structure, and the
* iterator implementation will have to replace the pointer in the radix tree
* internals. This allows the callback to reallocate the node to perform
* very special operations, normally not needed by normal applications.
*
* This callback is used to perform very low level analysis of the radix tree
* structure, scanning each possible node (but the root node), or in order to
* reallocate the nodes to reduce the allocation fragmentation (this is the
* Redis application for this callback).
*
* This is currently only supported in forward iterations (raxNext) */
typedef int (*raxNodeCallback)(raxNode **noderef);
/* Radix tree iterator state is encapsulated into this data structure. */
#define RAX_ITER_STATIC_LEN 128
#define RAX_ITER_JUST_SEEKED (1<<0) /* Iterator was just seeked. Return current
element for the first iteration and
clear the flag. */
#define RAX_ITER_EOF (1<<1) /* End of iteration reached. */
#define RAX_ITER_SAFE (1<<2) /* Safe iterator, allows operations while
iterating. But it is slower. */
typedef struct raxIterator {
int flags;
rax *rt; /* Radix tree we are iterating. */
unsigned char *key; /* The current string. */
void *data; /* Data associated to this key. */
size_t key_len; /* Current key length. */
size_t key_max; /* Max key len the current key buffer can hold. */
unsigned char key_static_string[RAX_ITER_STATIC_LEN];
raxNode *node; /* Current node. Only for unsafe iteration. */
raxStack stack; /* Stack used for unsafe iteration. */
raxNodeCallback node_cb; /* Optional node callback. Normally set to NULL. */
} raxIterator;
/* A special pointer returned for not found items. */
extern void *raxNotFound;
/* Exported API. */
rax *raxNew(void);
int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);
int raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);
int raxRemove(rax *rax, unsigned char *s, size_t len, void **old);
void *raxFind(rax *rax, unsigned char *s, size_t len);
void raxFree(rax *rax);
void raxFreeWithCallback(rax *rax, void (*free_callback)(void*));
void raxStart(raxIterator *it, rax *rt);
int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len);
int raxNext(raxIterator *it);
int raxPrev(raxIterator *it);
int raxRandomWalk(raxIterator *it, size_t steps);
int raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len);
void raxStop(raxIterator *it);
int raxEOF(raxIterator *it);
void raxShow(rax *rax);
uint64_t raxSize(rax *rax);
unsigned long raxTouch(raxNode *n);
void raxSetDebugMsg(int onoff);
/* Internal API. May be used by the node callback in order to access rax nodes
* in a low level way, so this function is exported as well. */
void raxSetData(raxNode *n, void *data);
#endif

View File

@ -1,43 +0,0 @@
/* Rax -- A radix tree implementation.
*
* Copyright (c) 2017, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Allocator selection.
*
* This file is used in order to change the Rax allocator at compile time.
* Just define the following defines to what you want to use. Also add
* the include of your alternate allocator if needed (not needed in order
* to use the default libc allocator). */
#ifndef RAX_ALLOC_H
#define RAX_ALLOC_H
#define rax_malloc malloc
#define rax_realloc realloc
#define rax_free free
#endif

View File

@ -1,26 +0,0 @@
#pragma once
#include "config.h"
#include <glib.h>
#if HAVE_POLKIT
# ifndef HAVE_POLKIT_AUTOPTR
# include <polkit/polkit.h>
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthority, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitSubject, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitDetails, g_object_unref)
# endif
#endif
#if !GLIB_CHECK_VERSION(2, 56, 0)
# define g_clear_handle_id(ptr, clear_func) \
G_STMT_START { \
guint __ptr = *(ptr); \
*(ptr) = 0; \
if (__ptr != 0) \
clear_func (__ptr); \
} G_STMT_END
#endif

View File

@ -1,343 +0,0 @@
/* sysprof-battery-source.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-battery-source"
#include "config.h"
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "sysprof-backport-autocleanups.h"
#include "sysprof-battery-source.h"
#include "sysprof-helpers.h"
struct _SysprofBatterySource
{
GObject parent_instance;
SysprofCaptureWriter *writer;
GArray *batteries;
guint combined_id;
guint poll_source;
};
typedef struct
{
gchar id[32];
gchar name[52];
guint charge_full;
guint charge_now;
gint charge_now_fd;
guint counter_id;
} Battery;
static void source_iface_init (SysprofSourceInterface *);
G_DEFINE_TYPE_WITH_CODE (SysprofBatterySource, sysprof_battery_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static void
battery_clear (gpointer data)
{
Battery *b = data;
if (b->charge_now_fd != -1)
close (b->charge_now_fd);
}
static gboolean
sysprof_battery_source_get_is_ready (SysprofSource *source)
{
return TRUE;
}
static void
sysprof_battery_source_prepare (SysprofSource *source)
{
SysprofBatterySource *self = (SysprofBatterySource *)source;
g_autoptr(GDir) dir = NULL;
g_autoptr(GArray) counters = NULL;
const gchar *name;
g_assert (SYSPROF_IS_BATTERY_SOURCE (self));
#define BAT_BASE_PATH "/sys/class/power_supply/"
counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter));
if (!(dir = g_dir_open (BAT_BASE_PATH, 0, NULL)))
goto emit_ready;
while ((name = g_dir_read_name (dir)))
{
g_autofree gchar *type_path = g_strdup_printf (BAT_BASE_PATH "%s/type", name);
g_autofree gchar *model_path = g_strdup_printf (BAT_BASE_PATH "%s/model_name", name);
g_autofree gchar *charge_now_path = g_strdup_printf (BAT_BASE_PATH "%s/charge_now", name);
g_autofree gchar *charge_full_path = g_strdup_printf (BAT_BASE_PATH "%s/charge_full", name);
g_autofree gchar *type_data = NULL;
g_autofree gchar *model_data = NULL;
g_autofree gchar *charge_full_data = NULL;
SysprofCaptureCounter ctr;
Battery bat = {{0}};
/* We dn't care about AC */
if (g_strcmp0 (name, "AC") == 0)
continue;
if (!g_file_get_contents (type_path, &type_data, NULL, NULL) ||
!g_str_has_prefix (type_data, "Battery"))
continue;
g_strlcpy (bat.id, name, sizeof bat.id);
if (g_file_get_contents (model_path, &model_data, NULL, NULL))
g_strlcpy (bat.name, model_data, sizeof bat.name);
if (g_file_get_contents (charge_full_path, &charge_full_data, NULL, NULL))
bat.charge_full = atoi (charge_full_data);
/* Wait for first polling */
bat.charge_now = 0;
g_strstrip (bat.id);
g_strstrip (bat.name);
bat.charge_now_fd = open (charge_now_path, O_RDONLY);
if (bat.charge_now_fd == -1)
continue;
bat.counter_id = sysprof_capture_writer_request_counter (self->writer, 1);
g_strlcpy (ctr.category, "Battery Charge", sizeof ctr.category);
g_strlcpy (ctr.name, bat.id, sizeof ctr.name);
g_snprintf (ctr.description, sizeof ctr.description, "%s (µAh)", bat.name);
ctr.id = bat.counter_id;
ctr.type = SYSPROF_CAPTURE_COUNTER_INT64;
g_array_append_val (self->batteries, bat);
g_array_append_val (counters, ctr);
}
if (counters->len > 0)
{
SysprofCaptureCounter ctr = {{0}};
self->combined_id = sysprof_capture_writer_request_counter (self->writer, 1);
/* Add combined counter */
g_strlcpy (ctr.category, "Battery Charge", sizeof ctr.category);
g_strlcpy (ctr.name, "Combined", sizeof ctr.name);
g_snprintf (ctr.description, sizeof ctr.description, "Combined Battery Charge (µAh)");
ctr.id = self->combined_id;
ctr.type = SYSPROF_CAPTURE_COUNTER_INT64;
g_array_append_val (counters, ctr);
sysprof_capture_writer_define_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
(gpointer)counters->data,
counters->len);
}
#undef BAT_BASE_PATH
emit_ready:
sysprof_source_emit_ready (source);
}
static gboolean
battery_poll (Battery *battery,
SysprofCaptureCounterValue *value)
{
gint64 val;
gssize len;
gchar buf[32];
g_assert (battery != NULL);
if (battery->charge_now_fd == -1)
return FALSE;
if (lseek (battery->charge_now_fd, 0, SEEK_SET) != 0)
{
close (battery->charge_now_fd);
battery->charge_now_fd = -1;
return FALSE;
}
len = read (battery->charge_now_fd, buf, sizeof buf - 1);
if (len < 0)
{
close (battery->charge_now_fd);
battery->charge_now_fd = -1;
return FALSE;
}
buf [len] = 0;
val = atoi (buf);
if (val != battery->charge_now)
{
battery->charge_now = val;
value->v64 = val;
return TRUE;
}
return FALSE;
}
static gboolean
sysprof_battery_source_poll_cb (gpointer data)
{
SysprofBatterySource *self = data;
g_autoptr(GArray) values = NULL;
g_autoptr(GArray) ids = NULL;
gint64 combined = 0;
g_assert (SYSPROF_IS_BATTERY_SOURCE (self));
values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue));
ids = g_array_new (FALSE, FALSE, sizeof (guint));
for (guint i = 0; i < self->batteries->len; i++)
{
Battery *battery = &g_array_index (self->batteries, Battery, i);
SysprofCaptureCounterValue value;
if G_LIKELY (battery_poll (battery, &value))
{
combined += value.v64;
g_array_append_val (ids, battery->counter_id);
g_array_append_val (values, value);
}
}
if (values->len > 0)
{
if (self->combined_id != 0)
{
SysprofCaptureCounterValue value = { .v64 = combined, };
g_array_append_val (ids, self->combined_id);
g_array_append_val (values, value);
}
sysprof_capture_writer_set_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
(gconstpointer)ids->data,
(gconstpointer)values->data,
ids->len);
}
return G_SOURCE_CONTINUE;
}
static void
sysprof_battery_source_start (SysprofSource *source)
{
SysprofBatterySource *self = (SysprofBatterySource *)source;
g_assert (SYSPROF_IS_BATTERY_SOURCE (self));
self->poll_source = g_timeout_add_seconds (1, sysprof_battery_source_poll_cb, self);
/* Poll immediately */
sysprof_battery_source_poll_cb (self);
}
static void
sysprof_battery_source_stop (SysprofSource *source)
{
SysprofBatterySource *self = (SysprofBatterySource *)source;
g_assert (SYSPROF_IS_BATTERY_SOURCE (self));
/* Poll one last time */
sysprof_battery_source_poll_cb (self);
g_clear_handle_id (&self->poll_source, g_source_remove);
sysprof_source_emit_finished (source);
}
static void
sysprof_battery_source_set_writer (SysprofSource *source,
SysprofCaptureWriter *writer)
{
SysprofBatterySource *self = (SysprofBatterySource *)source;
g_assert (SYSPROF_IS_BATTERY_SOURCE (self));
g_assert (writer != NULL);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
self->writer = sysprof_capture_writer_ref (writer);
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->get_is_ready = sysprof_battery_source_get_is_ready;
iface->prepare = sysprof_battery_source_prepare;
iface->set_writer = sysprof_battery_source_set_writer;
iface->start = sysprof_battery_source_start;
iface->stop = sysprof_battery_source_stop;
}
static void
sysprof_battery_source_finalize (GObject *object)
{
SysprofBatterySource *self = (SysprofBatterySource *)object;
g_clear_pointer (&self->batteries, g_array_unref);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
G_OBJECT_CLASS (sysprof_battery_source_parent_class)->finalize (object);
}
static void
sysprof_battery_source_class_init (SysprofBatterySourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_battery_source_finalize;
}
static void
sysprof_battery_source_init (SysprofBatterySource *self)
{
self->batteries = g_array_new (FALSE, FALSE, sizeof (Battery));
g_array_set_clear_func (self->batteries, battery_clear);
}
SysprofSource *
sysprof_battery_source_new (void)
{
return g_object_new (SYSPROF_TYPE_BATTERY_SOURCE, NULL);
}

View File

@ -1,35 +0,0 @@
/* sysprof-battery-source.h
*
* Copyright 2019 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
*/
#pragma once
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_BATTERY_SOURCE (sysprof_battery_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofBatterySource, sysprof_battery_source, SYSPROF, BATTERY_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_battery_source_new (void);
G_END_DECLS

View File

@ -1,551 +0,0 @@
/* sysprof-callgraph-profile.c
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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
*/
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2009-2012 Soeren Sandmann and others
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <glib/gi18n.h>
#include <string.h>
#include <sysprof-capture.h>
#include <unistd.h>
#include "../stackstash.h"
#include "sysprof-capture-util-private.h"
#include "sysprof-callgraph-profile.h"
#include "sysprof-capture-reader.h"
#include "sysprof-capture-symbol-resolver.h"
#include "sysprof-elf-symbol-resolver.h"
#include "sysprof-jitmap-symbol-resolver.h"
#include "sysprof-kernel-symbol-resolver.h"
#include "sysprof-map-lookaside.h"
#include "sysprof-selection.h"
#define CHECK_CANCELLABLE_INTERVAL 100
struct _SysprofCallgraphProfile
{
GObject parent_instance;
SysprofCaptureReader *reader;
SysprofSelection *selection;
StackStash *stash;
GStringChunk *symbols;
GHashTable *tags;
};
typedef struct
{
SysprofCaptureReader *reader;
SysprofSelection *selection;
} Generate;
static void profile_iface_init (SysprofProfileInterface *iface);
G_DEFINE_TYPE_EXTENDED (SysprofCallgraphProfile, sysprof_callgraph_profile, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_PROFILE, profile_iface_init))
enum {
PROP_0,
PROP_SELECTION,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
SysprofProfile *
sysprof_callgraph_profile_new (void)
{
return g_object_new (SYSPROF_TYPE_CALLGRAPH_PROFILE, NULL);
}
SysprofProfile *
sysprof_callgraph_profile_new_with_selection (SysprofSelection *selection)
{
return g_object_new (SYSPROF_TYPE_CALLGRAPH_PROFILE,
"selection", selection,
NULL);
}
static void
sysprof_callgraph_profile_finalize (GObject *object)
{
SysprofCallgraphProfile *self = (SysprofCallgraphProfile *)object;
g_clear_pointer (&self->symbols, g_string_chunk_free);
g_clear_pointer (&self->stash, stack_stash_unref);
g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
g_clear_pointer (&self->tags, g_hash_table_unref);
g_clear_object (&self->selection);
G_OBJECT_CLASS (sysprof_callgraph_profile_parent_class)->finalize (object);
}
static void
sysprof_callgraph_profile_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofCallgraphProfile *self = SYSPROF_CALLGRAPH_PROFILE (object);
switch (prop_id)
{
case PROP_SELECTION:
g_value_set_object (value, self->selection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_callgraph_profile_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofCallgraphProfile *self = SYSPROF_CALLGRAPH_PROFILE (object);
switch (prop_id)
{
case PROP_SELECTION:
self->selection = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_callgraph_profile_class_init (SysprofCallgraphProfileClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_callgraph_profile_finalize;
object_class->get_property = sysprof_callgraph_profile_get_property;
object_class->set_property = sysprof_callgraph_profile_set_property;
properties [PROP_SELECTION] =
g_param_spec_object ("selection",
"Selection",
"The selection for filtering the callgraph",
SYSPROF_TYPE_SELECTION,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_callgraph_profile_init (SysprofCallgraphProfile *self)
{
self->symbols = g_string_chunk_new (_sysprof_getpagesize ());
self->tags = g_hash_table_new (g_str_hash, g_str_equal);
}
static void
sysprof_callgraph_profile_set_reader (SysprofProfile *profile,
SysprofCaptureReader *reader)
{
SysprofCallgraphProfile *self = (SysprofCallgraphProfile *)profile;
g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self));
g_assert (reader != NULL);
g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
self->reader = sysprof_capture_reader_ref (reader);
}
static const gchar *
sysprof_callgraph_profile_intern_string_take (SysprofCallgraphProfile *self,
gchar *str)
{
const gchar *ret;
g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self));
g_assert (str != NULL);
ret = g_string_chunk_insert_const (self->symbols, str);
g_free (str);
return ret;
}
static const gchar *
sysprof_callgraph_profile_intern_string (SysprofCallgraphProfile *self,
const gchar *str)
{
g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self));
g_assert (str != NULL);
return g_string_chunk_insert_const (self->symbols, str);
}
static void
sysprof_callgraph_profile_generate_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SysprofCallgraphProfile *self = source_object;
Generate *gen = task_data;
SysprofCaptureReader *reader;
SysprofSelection *selection;
g_autoptr(GArray) resolved = NULL;
g_autoptr(GHashTable) maps_by_pid = NULL;
g_autoptr(GHashTable) cmdlines = NULL;
g_autoptr(GPtrArray) resolvers = NULL;
SysprofCaptureFrameType type;
StackStash *stash = NULL;
StackStash *resolved_stash = NULL;
guint count = 0;
gboolean ret = FALSE;
g_assert (G_IS_TASK (task));
g_assert (gen != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
reader = gen->reader;
selection = gen->selection;
maps_by_pid = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)sysprof_map_lookaside_free);
cmdlines = g_hash_table_new (NULL, NULL);
stash = stack_stash_new (NULL);
resolved_stash = stack_stash_new (NULL);
resolvers = g_ptr_array_new_with_free_func (g_object_unref);
g_ptr_array_add (resolvers, sysprof_capture_symbol_resolver_new ());
g_ptr_array_add (resolvers, sysprof_kernel_symbol_resolver_new ());
g_ptr_array_add (resolvers, sysprof_elf_symbol_resolver_new ());
g_ptr_array_add (resolvers, sysprof_jitmap_symbol_resolver_new ());
for (guint j = 0; j < resolvers->len; j++)
{
SysprofSymbolResolver *resolver = g_ptr_array_index (resolvers, j);
sysprof_capture_reader_reset (reader);
sysprof_symbol_resolver_load (resolver, reader);
}
sysprof_capture_reader_reset (reader);
/*
* The resolved pointer array is where we stash the names for the
* instruction pointers to pass to the stash stack. All the strings
* need to be deduplicated so that pointer comparison works as if we
* did instruction-pointer comparison.
*/
resolved = g_array_new (FALSE, TRUE, sizeof (guint64));
while (sysprof_capture_reader_peek_type (reader, &type))
{
const SysprofCaptureProcess *pr;
if (type != SYSPROF_CAPTURE_FRAME_PROCESS)
{
if (!sysprof_capture_reader_skip (reader))
goto failure;
continue;
}
if (NULL == (pr = sysprof_capture_reader_read_process (reader)))
goto failure;
if (!g_hash_table_contains (cmdlines, GINT_TO_POINTER (pr->frame.pid)))
{
g_autofree gchar *cmdline = g_strdup_printf ("[%s]", pr->cmdline);
g_hash_table_insert (cmdlines,
GINT_TO_POINTER (pr->frame.pid),
(gchar *)sysprof_callgraph_profile_intern_string (self, cmdline));
}
}
if (g_task_return_error_if_cancelled (task))
goto cleanup;
sysprof_capture_reader_reset (reader);
/*
* Walk through all of the sample events and resolve instruction-pointers
* to symbol names by loading the particular map and extracting the symbol
* name. If we wanted to support dynamic systems, we'd want to extend this
* to parse information from captured data about the languages jit'd code.
*/
while (sysprof_capture_reader_peek_type (reader, &type))
{
SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE;
const SysprofCaptureSample *sample;
StackNode *node;
StackNode *iter;
const gchar *cmdline;
guint len = 5;
if (type != SYSPROF_CAPTURE_FRAME_SAMPLE)
{
if (!sysprof_capture_reader_skip (reader))
goto failure;
continue;
}
if (++count == CHECK_CANCELLABLE_INTERVAL)
{
if (g_task_return_error_if_cancelled (task))
goto cleanup;
}
if (NULL == (sample = sysprof_capture_reader_read_sample (reader)))
goto failure;
if (!sysprof_selection_contains (selection, sample->frame.time))
continue;
if (sample->n_addrs == 0)
continue;
cmdline = g_hash_table_lookup (cmdlines, GINT_TO_POINTER (sample->frame.pid));
if (cmdline == NULL)
{
gchar *pidstr = g_strdup_printf ("[Process %d]", sample->frame.pid);
g_hash_table_insert (cmdlines, GINT_TO_POINTER (sample->frame.pid), pidstr);
cmdline = pidstr;
}
#if 0
/* This assertion appears to hold true, but since we're taking in
* untrusted data from capture files, it's not safe to assume. But in
* practice it is.
*/
g_assert (sysprof_address_is_context_switch (sample->addrs[0], &last_context));
last_context = SYSPROF_ADDRESS_CONTEXT_NONE;
#endif
node = stack_stash_add_trace (stash, (gpointer)sample->addrs, sample->n_addrs, 1);
for (iter = node; iter != NULL; iter = iter->parent)
len++;
if (G_UNLIKELY (resolved->len < len))
g_array_set_size (resolved, len);
len = 0;
for (iter = node; iter != NULL; iter = iter->parent)
{
SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE;
SysprofAddress address = iter->data;
const gchar *symbol = NULL;
if (sysprof_address_is_context_switch (address, &context))
{
if (last_context)
symbol = sysprof_address_context_to_string (last_context);
else
symbol = NULL;
last_context = context;
}
else
{
/* In case we get plain backtraces that aren't coming from perf,
* we might never get a context switch into user-space. This ensures
* that we still get traces for things from backtrace().
*/
if (last_context == SYSPROF_ADDRESS_CONTEXT_NONE)
last_context = SYSPROF_ADDRESS_CONTEXT_USER;
for (guint j = 0; j < resolvers->len; j++)
{
SysprofSymbolResolver *resolver = g_ptr_array_index (resolvers, j);
GQuark tag = 0;
gchar *str;
str = sysprof_symbol_resolver_resolve_with_context (resolver,
sample->frame.time,
sample->frame.pid,
last_context,
address,
&tag);
if (str != NULL)
{
symbol = sysprof_callgraph_profile_intern_string_take (self, str);
if (tag != 0)
g_hash_table_insert (self->tags, (gchar *)symbol, GSIZE_TO_POINTER (tag));
break;
}
}
}
if (symbol != NULL)
g_array_index (resolved, SysprofAddress, len++) = POINTER_TO_U64 (symbol);
}
if (last_context && last_context != SYSPROF_ADDRESS_CONTEXT_USER)
{
/* Kernel threads do not have a user part, so we end up here
* without ever getting a user context. If this happens,
* add the '- - kernel - - ' name, so that kernel threads
* are properly blamed on the kernel
*/
const gchar *name = sysprof_address_context_to_string (last_context);
g_array_index (resolved, SysprofAddress, len++) = POINTER_TO_U64 (name);
}
g_array_index (resolved, guint64, len++) = POINTER_TO_U64 (cmdline);
g_array_index (resolved, guint64, len++) = POINTER_TO_U64 ("[Everything]");
stack_stash_add_trace (resolved_stash, (gpointer)resolved->data, len, 1);
}
ret = TRUE;
failure:
if (ret == FALSE)
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"%s",
_("Sysprof was unable to generate a callgraph from the system capture."));
else
g_task_return_pointer (task, g_steal_pointer (&resolved_stash), (GDestroyNotify)stack_stash_unref);
cleanup:
g_clear_pointer (&resolved_stash, stack_stash_unref);
g_clear_pointer (&stash, stack_stash_unref);
}
static void
generate_free (Generate *generate)
{
sysprof_capture_reader_unref (generate->reader);
g_clear_object (&generate->selection);
g_slice_free (Generate, generate);
}
static void
sysprof_callgraph_profile_generate (SysprofProfile *profile,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
SysprofCallgraphProfile *self = (SysprofCallgraphProfile *)profile;
Generate *gen;
g_autoptr(GTask) task = NULL;
g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
gen = g_slice_new0 (Generate);
gen->reader = sysprof_capture_reader_copy (self->reader);
gen->selection = sysprof_selection_copy (self->selection);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, gen, (GDestroyNotify)generate_free);
g_task_run_in_thread (task, sysprof_callgraph_profile_generate_worker);
}
static gboolean
sysprof_callgraph_profile_generate_finish (SysprofProfile *profile,
GAsyncResult *result,
GError **error)
{
SysprofCallgraphProfile *self = (SysprofCallgraphProfile *)profile;
StackStash *stash;
g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self));
g_assert (G_IS_TASK (result));
stash = g_task_propagate_pointer (G_TASK (result), error);
if (stash != NULL)
{
if (stash != self->stash)
{
g_clear_pointer (&self->stash, stack_stash_unref);
self->stash = g_steal_pointer (&stash);
}
g_clear_pointer (&stash, stack_stash_unref);
return TRUE;
}
return FALSE;
}
static void
profile_iface_init (SysprofProfileInterface *iface)
{
iface->generate = sysprof_callgraph_profile_generate;
iface->generate_finish = sysprof_callgraph_profile_generate_finish;
iface->set_reader = sysprof_callgraph_profile_set_reader;
}
gpointer
sysprof_callgraph_profile_get_stash (SysprofCallgraphProfile *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PROFILE (self), NULL);
return self->stash;
}
gboolean
sysprof_callgraph_profile_is_empty (SysprofCallgraphProfile *self)
{
StackNode *root;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PROFILE (self), FALSE);
return (self->stash == NULL ||
!(root = stack_stash_get_root (self->stash)) ||
!root->total);
}
GQuark
sysprof_callgraph_profile_get_tag (SysprofCallgraphProfile *self,
const gchar *symbol)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PROFILE (self), 0);
return GPOINTER_TO_SIZE (g_hash_table_lookup (self->tags, symbol));
}

View File

@ -1,51 +0,0 @@
/* sysprof-callgraph-profile.h
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-version-macros.h"
#include "sysprof-profile.h"
#include "sysprof-selection.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CALLGRAPH_PROFILE (sysprof_callgraph_profile_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofCallgraphProfile, sysprof_callgraph_profile, SYSPROF, CALLGRAPH_PROFILE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofProfile *sysprof_callgraph_profile_new (void);
SYSPROF_AVAILABLE_IN_ALL
SysprofProfile *sysprof_callgraph_profile_new_with_selection (SysprofSelection *selection);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_callgraph_profile_is_empty (SysprofCallgraphProfile *self);
SYSPROF_AVAILABLE_IN_ALL
gpointer sysprof_callgraph_profile_get_stash (SysprofCallgraphProfile *self);
SYSPROF_AVAILABLE_IN_ALL
GQuark sysprof_callgraph_profile_get_tag (SysprofCallgraphProfile *self,
const gchar *symbol);
G_END_DECLS

View File

@ -1,41 +0,0 @@
/* sysprof-capture-gobject.h
*
* Copyright 2020 Endless Mobile, Inc.
*
* Author:
* - Philip Withnall <withnall@endlessm.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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include <glib.h>
#include "sysprof-capture.h"
G_BEGIN_DECLS
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureCondition, sysprof_capture_condition_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureCursor, sysprof_capture_cursor_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureReader, sysprof_capture_reader_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureWriter, sysprof_capture_writer_unref)
G_END_DECLS

View File

@ -1,92 +0,0 @@
/* sysprof-capture-gobject.c
*
* Copyright 2019 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 <sysprof-capture.h>
#include "sysprof-capture-gobject.h"
G_DEFINE_BOXED_TYPE (SysprofCaptureReader, sysprof_capture_reader, (GBoxedCopyFunc)sysprof_capture_reader_ref, (GBoxedFreeFunc)sysprof_capture_reader_unref)
G_DEFINE_BOXED_TYPE (SysprofCaptureWriter, sysprof_capture_writer, (GBoxedCopyFunc)sysprof_capture_writer_ref, (GBoxedFreeFunc)sysprof_capture_writer_unref)
G_DEFINE_BOXED_TYPE (SysprofCaptureCursor, sysprof_capture_cursor, (GBoxedCopyFunc)sysprof_capture_cursor_ref, (GBoxedFreeFunc)sysprof_capture_cursor_unref)
SysprofCaptureReader *
sysprof_capture_reader_new_with_error (const char *filename,
GError **error)
{
SysprofCaptureReader *ret;
if (!(ret = sysprof_capture_reader_new (filename)))
g_set_error_literal (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
g_strerror (errno));
return ret;
}
SysprofCaptureReader *
sysprof_capture_reader_new_from_fd_with_error (int fd,
GError **error)
{
SysprofCaptureReader *ret;
if (!(ret = sysprof_capture_reader_new_from_fd (fd)))
g_set_error_literal (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
g_strerror (errno));
return ret;
}
SysprofCaptureReader *
sysprof_capture_writer_create_reader_with_error (SysprofCaptureWriter *self,
GError **error)
{
SysprofCaptureReader *ret;
if (!(ret = sysprof_capture_writer_create_reader (self)))
g_set_error_literal (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
g_strerror (errno));
return ret;
}
bool
sysprof_capture_reader_save_as_with_error (SysprofCaptureReader *self,
const char *filename,
GError **error)
{
if (!sysprof_capture_reader_save_as (self, filename))
{
g_set_error_literal (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
g_strerror (errno));
return false;
}
return true;
}

View File

@ -1,58 +0,0 @@
/* sysprof-capture-gobject.h
*
* Copyright 2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include <glib-object.h>
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CAPTURE_READER (sysprof_capture_reader_get_type())
#define SYSPROF_TYPE_CAPTURE_WRITER (sysprof_capture_writer_get_type())
#define SYSPROF_TYPE_CAPTURE_CURSOR (sysprof_capture_cursor_get_type())
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_capture_reader_get_type (void);
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_capture_writer_get_type (void);
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_capture_cursor_get_type (void);
SYSPROF_AVAILABLE_IN_3_38
SysprofCaptureReader *sysprof_capture_reader_new_with_error (const char *filename,
GError **error);
SYSPROF_AVAILABLE_IN_3_38
SysprofCaptureReader *sysprof_capture_reader_new_from_fd_with_error (int fd,
GError **error);
SYSPROF_AVAILABLE_IN_3_38
SysprofCaptureReader *sysprof_capture_writer_create_reader_with_error (SysprofCaptureWriter *self,
GError **error);
SYSPROF_AVAILABLE_IN_3_38
bool sysprof_capture_reader_save_as_with_error (SysprofCaptureReader *self,
const char *filename,
GError **error);
G_END_DECLS

View File

@ -1,137 +0,0 @@
/* sysprof-capture-symbol-resolver.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-capture-symbol-resolver"
#include "config.h"
#include <unistd.h>
#include "sysprof-capture-symbol-resolver.h"
#include "sysprof-platform.h"
#include "sysprof-private.h"
#include "sysprof-symbol-map.h"
/**
* SECTION:sysprof-capture-symbol-resolver:
* @title: SysprofCaptureSymbolResolver
* @short_description: resolve symbols from embedded data within the capture
*
* This looks for an embedded file "__symbols__" in the capture and tries to
* decode them using the data stored within that file.
*
* This is useful when moving capture files between machines as it will allow
* the viewer machine to get access to the symbols without having to copy them
* to the local system.
*
* Since: 3.34
*/
struct _SysprofCaptureSymbolResolver
{
GObject parent_instance;
SysprofSymbolMap *map;
};
static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface);
G_DEFINE_TYPE_WITH_CODE (SysprofCaptureSymbolResolver, sysprof_capture_symbol_resolver, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER, symbol_resolver_iface_init))
static void
sysprof_capture_symbol_resolver_finalize (GObject *object)
{
SysprofCaptureSymbolResolver *self = (SysprofCaptureSymbolResolver *)object;
g_clear_pointer (&self->map, sysprof_symbol_map_free);
G_OBJECT_CLASS (sysprof_capture_symbol_resolver_parent_class)->finalize (object);
}
static void
sysprof_capture_symbol_resolver_class_init (SysprofCaptureSymbolResolverClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_capture_symbol_resolver_finalize;
}
static void
sysprof_capture_symbol_resolver_init (SysprofCaptureSymbolResolver *self)
{
self->map = sysprof_symbol_map_new ();
}
SysprofSymbolResolver *
sysprof_capture_symbol_resolver_new (void)
{
return g_object_new (SYSPROF_TYPE_CAPTURE_SYMBOL_RESOLVER, NULL);
}
static void
sysprof_capture_symbol_resolver_load (SysprofSymbolResolver *resolver,
SysprofCaptureReader *reader)
{
SysprofCaptureSymbolResolver *self = (SysprofCaptureSymbolResolver *)resolver;
gint byte_order;
gint fd;
g_assert (SYSPROF_IS_CAPTURE_SYMBOL_RESOLVER (self));
g_assert (reader != NULL);
byte_order = sysprof_capture_reader_get_byte_order (reader);
if (-1 == (fd = sysprof_memfd_create ("[symbol-decoder]")))
return;
if (sysprof_capture_reader_read_file_fd (reader, "__symbols__", fd))
{
lseek (fd, 0, SEEK_SET);
sysprof_symbol_map_deserialize (self->map, byte_order, fd);
}
close (fd);
}
gchar *
sysprof_capture_symbol_resolver_resolve_with_context (SysprofSymbolResolver *resolver,
guint64 time,
GPid pid,
SysprofAddressContext context,
SysprofCaptureAddress address,
GQuark *tag)
{
SysprofCaptureSymbolResolver *self = (SysprofCaptureSymbolResolver *)resolver;
const gchar *name;
g_assert (SYSPROF_IS_CAPTURE_SYMBOL_RESOLVER (self));
if ((name = sysprof_symbol_map_lookup (self->map, time, pid, address, tag)))
return g_strdup (name);
return NULL;
}
static void
symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface)
{
iface->load = sysprof_capture_symbol_resolver_load;
iface->resolve_with_context = sysprof_capture_symbol_resolver_resolve_with_context;
}

View File

@ -1,35 +0,0 @@
/* sysprof-capture-symbol-resolver.h
*
* Copyright 2019 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
*/
#pragma once
#include "sysprof-symbol-resolver.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CAPTURE_SYMBOL_RESOLVER (sysprof_capture_symbol_resolver_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofCaptureSymbolResolver, sysprof_capture_symbol_resolver, SYSPROF, CAPTURE_SYMBOL_RESOLVER, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbolResolver *sysprof_capture_symbol_resolver_new (void);
G_END_DECLS

View File

@ -1,313 +0,0 @@
/* sysprof-control-source.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
*/
#define G_LOG_DOMAIN "sysprof-control-source"
#include "config.h"
#include <fcntl.h>
#include <glib-unix.h>
#include <glib/gstdio.h>
#include <gio/gunixfdlist.h>
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
#include <gio/gunixconnection.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "mapped-ring-buffer.h"
#include "mapped-ring-buffer-source.h"
#include "sysprof-control-source.h"
#define CREATRING "CreatRing\0"
#define CREATRING_LEN 10
struct _SysprofControlSource
{
GObject parent_instance;
SysprofCaptureWriter *writer;
GArray *source_ids;
#ifdef G_OS_UNIX
GUnixConnection *conn;
#endif
GCancellable *cancellable;
/* Control messages are 10 bytes */
gchar read_buf[10];
guint stopped : 1;
};
typedef struct
{
SysprofControlSource *self;
guint id;
} RingData;
static void source_iface_init (SysprofSourceInterface *iface);
G_DEFINE_TYPE_WITH_CODE (SysprofControlSource, sysprof_control_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static void
ring_data_free (RingData *rd)
{
g_clear_object (&rd->self);
g_slice_free (RingData, rd);
}
SysprofControlSource *
sysprof_control_source_new (void)
{
return g_object_new (SYSPROF_TYPE_CONTROL_SOURCE, NULL);
}
static void
remove_source_id (gpointer data)
{
guint *id = data;
g_source_remove (*id);
}
static void
sysprof_control_source_finalize (GObject *object)
{
SysprofControlSource *self = (SysprofControlSource *)object;
#ifdef G_OS_UNIX
g_clear_object (&self->conn);
#endif
if (self->source_ids->len > 0)
g_array_remove_range (self->source_ids, 0, self->source_ids->len);
g_clear_pointer (&self->source_ids, g_array_unref);
G_OBJECT_CLASS (sysprof_control_source_parent_class)->finalize (object);
}
static void
sysprof_control_source_class_init (SysprofControlSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_control_source_finalize;
}
static void
sysprof_control_source_init (SysprofControlSource *self)
{
self->cancellable = g_cancellable_new ();
self->source_ids = g_array_new (FALSE, FALSE, sizeof (guint));
g_array_set_clear_func (self->source_ids, remove_source_id);
}
static bool
event_frame_cb (const void *data,
size_t *length,
void *user_data)
{
const SysprofCaptureFrame *fr = data;
RingData *rd = user_data;
g_assert (rd != NULL);
g_assert (SYSPROF_IS_CONTROL_SOURCE (rd->self));
g_assert (rd->id > 0);
if G_UNLIKELY (rd->self->writer == NULL ||
*length < sizeof *fr ||
*length < fr->len ||
fr->type >= SYSPROF_CAPTURE_FRAME_LAST)
goto remove_source;
_sysprof_capture_writer_add_raw (rd->self->writer, fr);
*length = fr->len;
return G_SOURCE_CONTINUE;
remove_source:
for (guint i = 0; i < rd->self->source_ids->len; i++)
{
guint id = g_array_index (rd->self->source_ids, guint, i);
if (id == rd->id)
{
g_array_remove_index (rd->self->source_ids, i);
break;
}
}
return G_SOURCE_REMOVE;
}
#ifdef G_OS_UNIX
static void
sysprof_control_source_read_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(SysprofControlSource) self = user_data;
GInputStream *input_stream = (GInputStream *)object;
gssize ret;
g_assert (SYSPROF_IS_CONTROL_SOURCE (self));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_INPUT_STREAM (input_stream));
ret = g_input_stream_read_finish (G_INPUT_STREAM (input_stream), result, NULL);
if (ret == sizeof self->read_buf)
{
if (memcmp (self->read_buf, CREATRING, CREATRING_LEN) == 0)
{
g_autoptr(MappedRingBuffer) buffer = NULL;
if ((buffer = mapped_ring_buffer_new_reader (0)))
{
int fd = mapped_ring_buffer_get_fd (buffer);
RingData *rd;
rd = g_slice_new0 (RingData);
rd->self = g_object_ref (self);
rd->id = mapped_ring_buffer_create_source_full (buffer,
event_frame_cb,
rd,
(GDestroyNotify)ring_data_free);
g_array_append_val (self->source_ids, rd->id);
g_unix_connection_send_fd (self->conn, fd, NULL, NULL);
}
}
if (!g_cancellable_is_cancelled (self->cancellable))
g_input_stream_read_async (G_INPUT_STREAM (input_stream),
self->read_buf,
sizeof self->read_buf,
G_PRIORITY_HIGH,
self->cancellable,
sysprof_control_source_read_cb,
g_object_ref (self));
}
}
#endif
static void
sysprof_control_source_modify_spawn (SysprofSource *source,
SysprofSpawnable *spawnable)
{
#ifdef G_OS_UNIX
SysprofControlSource *self = (SysprofControlSource *)source;
g_autofree gchar *child_no_str = NULL;
g_autoptr(GSocketConnection) stream = NULL;
g_autoptr(GSocket) sock = NULL;
GInputStream *input_stream;
int fds[2];
int child_no;
g_assert (SYSPROF_IS_SOURCE (source));
g_assert (SYSPROF_IS_SPAWNABLE (spawnable));
/* Create a socket pair to communicate D-Bus protocol over */
if (socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
return;
g_unix_set_fd_nonblocking (fds[0], TRUE, NULL);
g_unix_set_fd_nonblocking (fds[1], TRUE, NULL);
/* @child_no is assigned the FD the child will receive. We can
* use that to set the environment vaiable of the control FD.
*/
child_no = sysprof_spawnable_take_fd (spawnable, fds[1], -1);
child_no_str = g_strdup_printf ("%d", child_no);
sysprof_spawnable_setenv (spawnable, "SYSPROF_CONTROL_FD", child_no_str);
/* We need an IOStream for GDBusConnection to use. Since we need
* the ability to pass FDs, it must be a GUnixSocketConnection.
*/
if (!(sock = g_socket_new_from_fd (fds[0], NULL)))
{
close (fds[0]);
g_critical ("Failed to create GSocket");
return;
}
g_socket_set_blocking (sock, FALSE);
stream = g_socket_connection_factory_create_connection (sock);
g_assert (G_IS_UNIX_CONNECTION (stream));
self->conn = g_object_ref (G_UNIX_CONNECTION (stream));
input_stream = g_io_stream_get_input_stream (G_IO_STREAM (stream));
g_input_stream_read_async (input_stream,
self->read_buf,
sizeof self->read_buf,
G_PRIORITY_HIGH,
self->cancellable,
sysprof_control_source_read_cb,
g_object_ref (self));
#endif
}
static void
sysprof_control_source_stop (SysprofSource *source)
{
SysprofControlSource *self = (SysprofControlSource *)source;
g_assert (SYSPROF_IS_CONTROL_SOURCE (self));
self->stopped = TRUE;
g_cancellable_cancel (self->cancellable);
if (self->source_ids->len > 0)
g_array_remove_range (self->source_ids, 0, self->source_ids->len);
sysprof_source_emit_finished (source);
}
static void
sysprof_control_source_set_writer (SysprofSource *source,
SysprofCaptureWriter *writer)
{
SysprofControlSource *self = (SysprofControlSource *)source;
g_assert (SYSPROF_IS_CONTROL_SOURCE (self));
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
if (writer != NULL)
self->writer = sysprof_capture_writer_ref (writer);
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->set_writer = sysprof_control_source_set_writer;
iface->modify_spawn = sysprof_control_source_modify_spawn;
iface->stop = sysprof_control_source_stop;
}

View File

@ -1,35 +0,0 @@
/* sysprof-control-source.h
*
* 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
*/
#pragma once
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CONTROL_SOURCE (sysprof_control_source_get_type())
SYSPROF_AVAILABLE_IN_3_38
G_DECLARE_FINAL_TYPE (SysprofControlSource, sysprof_control_source, SYSPROF, CONTROL_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_3_38
SysprofControlSource *sysprof_control_source_new (void);
G_END_DECLS

View File

@ -1,472 +0,0 @@
/* sysprof-diskstat-source.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-diskstat-source"
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <glib/gstdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sysprof-backport-autocleanups.h"
#include "sysprof-line-reader.h"
#include "sysprof-diskstat-source.h"
#include "sysprof-helpers.h"
#define ADD_FROM_CHAR(v, c) (((v)*10L)+((c)-'0'))
struct _SysprofDiskstatSource
{
GObject parent_instance;
SysprofCaptureWriter *writer;
GArray *diskstats;
/* FD for /proc/net/dev contents */
gint diskstat_fd;
/* GSource ID for polling */
guint poll_source;
guint ignore_next_poll : 1;
};
typedef struct
{
/* Counter IDs */
guint reads_total_id;
guint writes_total_id;
gchar device[32];
gint64 reads_total;
gint64 reads_merged;
gint64 reads_sectors;
gint64 reads_msec;
gint64 writes_total;
gint64 writes_merged;
gint64 writes_sectors;
gint64 writes_msec;
gint64 iops_active;
gint64 iops_msec;
gint64 iops_msec_weighted;
} Diskstat;
enum {
/* -2 */ COLUMN_MAJOR,
/* -1 */ COLUMN_MINOR,
/* 0 */ COLUMN_NAME,
/* 1 */ COLUMN_READS_TOTAL,
/* 2 */ COLUMN_READS_MERGED,
/* 3 */ COLUMN_READS_SECTORS,
/* 4 */ COLUMN_READS_MSEC,
/* 5 */ COLUMN_WRITES_TOTAL,
/* 6 */ COLUMN_WRITES_MERGED,
/* 7 */ COLUMN_WRITES_SECTORS,
/* 8 */ COLUMN_WRITES_MSEC,
/* 9 */ COLUMN_IOPS_ACTIVE,
/* 10 */ COLUMN_IOPS_MSEC,
/* 11 */ COLUMN_IOPS_MSEC_WEIGHTED,
};
static void source_iface_init (SysprofSourceInterface *);
G_DEFINE_TYPE_WITH_CODE (SysprofDiskstatSource, sysprof_diskstat_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static Diskstat *
find_device_by_name (SysprofDiskstatSource *self,
const gchar *name)
{
g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self));
g_assert (self->writer != NULL);
g_assert (name != NULL);
for (guint i = 0; i < self->diskstats->len; i++)
{
Diskstat *diskstat = &g_array_index (self->diskstats, Diskstat, i);
if (strcmp (name, diskstat->device) == 0)
return diskstat;
}
return NULL;
}
static Diskstat *
register_counters_by_name (SysprofDiskstatSource *self,
const gchar *name)
{
SysprofCaptureCounter ctr[2] = {{{0}}};
Diskstat ds = {0};
g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self));
g_assert (name != NULL);
g_assert (self->writer != NULL);
ds.reads_total_id = sysprof_capture_writer_request_counter (self->writer, 1);
ds.writes_total_id = sysprof_capture_writer_request_counter (self->writer, 1);
g_strlcpy (ds.device, name, sizeof ds.device);
g_strlcpy (ctr[0].category, "Disk", sizeof ctr[0].category);
g_snprintf (ctr[0].name, sizeof ctr[0].name, "Total Reads (%s)", name);
g_strlcpy (ctr[0].description, name, sizeof ctr[0].description);
ctr[0].id = ds.reads_total_id;
ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64;
ctr[0].value.v64 = 0;
g_strlcpy (ctr[1].category, "Disk", sizeof ctr[1].category);
g_snprintf (ctr[1].name, sizeof ctr[1].name, "Total Writes (%s)", name);
g_strlcpy (ctr[1].description, name, sizeof ctr[1].description);
ctr[1].id = ds.writes_total_id;
ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64;
ctr[1].value.v64 = 1;
sysprof_capture_writer_define_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
ctr, G_N_ELEMENTS (ctr));
g_array_append_val (self->diskstats, ds);
return &g_array_index (self->diskstats, Diskstat, self->diskstats->len - 1);
}
static gboolean
sysprof_diskstat_source_get_is_ready (SysprofSource *source)
{
return TRUE;
}
static gboolean
sysprof_diskstat_source_poll_cb (gpointer data)
{
g_autoptr(SysprofLineReader) reader = NULL;
SysprofDiskstatSource *self = data;
g_autoptr(GArray) counters = NULL;
g_autoptr(GArray) values = NULL;
gchar buf[4096*4];
Diskstat *found;
gint64 combined_reads_total = 0;
gint64 combined_writes_total = 0;
gssize len;
gsize line_len;
gchar *line;
g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self));
if (self->diskstat_fd == -1)
{
self->poll_source = 0;
return G_SOURCE_REMOVE;
}
/* Seek to 0 forces reload of data */
lseek (self->diskstat_fd, 0, SEEK_SET);
len = read (self->diskstat_fd, buf, sizeof buf - 1);
/* Bail for now unless we read enough data */
if (len > 0)
buf[len] = 0;
else
return G_SOURCE_CONTINUE;
counters = g_array_new (FALSE, FALSE, sizeof (guint));
values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue));
reader = sysprof_line_reader_new (buf, len);
#if 0
Entries looks like this...
------------------------------------------------------------------------------------------------------------------------------
259 0 nvme0n1 455442 37 15274065 82579 714090 524038 44971827 1078532 0 244176 957543 0 0 0 0
259 1 nvme0n1p1 403 0 10411 102 61 39 33171 72 0 61 53 0 0 0 0
259 2 nvme0n1p2 138 0 9594 29 7 1 64 20 0 49 24 0 0 0 0
259 3 nvme0n1p3 454820 37 15249700 82432 688488 523998 44938592 1038914 0 237085 924545 0 0 0 0
253 0 dm-0 454789 0 15246924 107870 1238001 0 44938592 7873687 0 248354 7981557 0 0 0 0
253 1 dm-1 92473 0 4824170 27656 179001 0 5876760 1378161 0 45291 1405817 0 0 0 0
253 2 dm-2 206 0 5552 100 240 0 1920 297 0 219 397 0 0 0 0
253 3 dm-3 362064 0 10414850 80861 1058760 0 39245920 6496983 0 206426 6577844 0 0 0 0
------------------------------------------------------------------------------------------------------------------------------
#endif
while ((line = (gchar *)sysprof_line_reader_next (reader, &line_len)))
{
Diskstat ds = {0};
gint64 dummy = 0;
gint column = COLUMN_MAJOR;
line[line_len] = 0;
/* Skip past initial space */
while (g_ascii_isspace (*line))
line++;
for (const gchar *ptr = line; *ptr; ptr++)
{
gchar ch;
/* Skip past space and advance to next column */
if (g_ascii_isspace (*ptr))
{
while (g_ascii_isspace (*ptr))
ptr++;
column++;
}
ch = *ptr;
switch (column)
{
case COLUMN_MAJOR:
case COLUMN_MINOR:
default:
dummy = ADD_FROM_CHAR (dummy, ch);
break;
case COLUMN_NAME:
{
guint j;
for (j = 0; j < sizeof ds.device && ds.device[j] != 0; j++) { /* Do Nothing */ }
if (j < sizeof ds.device)
ds.device[j] = ch;
ds.device[sizeof ds.device - 1] = 0;
break;
}
case COLUMN_READS_TOTAL:
ds.reads_total = ADD_FROM_CHAR (ds.reads_total, ch);
break;
case COLUMN_READS_MERGED:
ds.reads_merged = ADD_FROM_CHAR (ds.reads_merged, ch);
break;
case COLUMN_READS_SECTORS:
ds.reads_sectors = ADD_FROM_CHAR (ds.reads_sectors, ch);
break;
case COLUMN_READS_MSEC:
ds.reads_msec = ADD_FROM_CHAR (ds.reads_msec, ch);
break;
case COLUMN_WRITES_TOTAL:
ds.writes_total = ADD_FROM_CHAR (ds.writes_total, ch);
break;
case COLUMN_WRITES_MERGED:
ds.writes_merged = ADD_FROM_CHAR (ds.writes_merged, ch);
break;
case COLUMN_WRITES_SECTORS:
ds.writes_sectors = ADD_FROM_CHAR (ds.writes_sectors, ch);
break;
case COLUMN_WRITES_MSEC:
ds.writes_msec = ADD_FROM_CHAR (ds.writes_msec, ch);
break;
case COLUMN_IOPS_ACTIVE:
ds.iops_active = ADD_FROM_CHAR (ds.iops_active, ch);
break;
case COLUMN_IOPS_MSEC:
ds.iops_msec = ADD_FROM_CHAR (ds.iops_msec, ch);
break;
case COLUMN_IOPS_MSEC_WEIGHTED:
ds.iops_msec_weighted = ADD_FROM_CHAR (ds.iops_msec_weighted, ch);
break;
}
}
g_strstrip (ds.device);
if (ds.device[0])
{
gint64 reads_total;
gint64 writes_total;
if (!(found = find_device_by_name (self, ds.device)))
found = register_counters_by_name (self, ds.device);
/* Stash new value, based on diff from previous */
reads_total = ds.reads_total - found->reads_total;
writes_total = ds.writes_total - found->writes_total;
g_array_append_val (counters, found->reads_total_id);
g_array_append_val (values, reads_total);
g_array_append_val (counters, found->writes_total_id);
g_array_append_val (values, writes_total);
/* Update combined values */
combined_reads_total += reads_total;
combined_writes_total += writes_total;
/* Save current value for diff on next poll */
found->reads_total = ds.reads_total;
found->writes_total = ds.writes_total;
}
}
if (!(found = find_device_by_name (self, "Combined")))
found = register_counters_by_name (self, "Combined");
g_array_append_val (counters, found->reads_total_id);
g_array_append_val (values, combined_reads_total);
g_array_append_val (counters, found->writes_total_id);
g_array_append_val (values, combined_writes_total);
if (self->ignore_next_poll)
self->ignore_next_poll = FALSE;
else
sysprof_capture_writer_set_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
(const guint *)(gpointer)counters->data,
(const SysprofCaptureCounterValue *)(gpointer)values->data,
counters->len);
return G_SOURCE_CONTINUE;
}
static void
sysprof_diskstat_source_prepare (SysprofSource *source)
{
SysprofDiskstatSource *self = (SysprofDiskstatSource *)source;
g_autoptr(GError) error = NULL;
g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self));
self->diskstat_fd = g_open ("/proc/diskstats", O_RDONLY, 0);
if (self->diskstat_fd == -1)
{
int errsv = errno;
error = g_error_new (G_FILE_ERROR,
g_file_error_from_errno (errsv),
"%s",
g_strerror (errsv));
sysprof_source_emit_failed (source, error);
return;
}
self->ignore_next_poll = TRUE;
sysprof_diskstat_source_poll_cb (self);
sysprof_source_emit_ready (source);
}
static void
sysprof_diskstat_source_start (SysprofSource *source)
{
SysprofDiskstatSource *self = (SysprofDiskstatSource *)source;
g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self));
self->poll_source = g_timeout_add (200, sysprof_diskstat_source_poll_cb, self);
/* Poll immediately */
sysprof_diskstat_source_poll_cb (self);
}
static void
sysprof_diskstat_source_stop (SysprofSource *source)
{
SysprofDiskstatSource *self = (SysprofDiskstatSource *)source;
g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self));
/* Poll one last time */
sysprof_diskstat_source_poll_cb (self);
g_clear_handle_id (&self->poll_source, g_source_remove);
sysprof_source_emit_finished (source);
}
static void
sysprof_diskstat_source_set_writer (SysprofSource *source,
SysprofCaptureWriter *writer)
{
SysprofDiskstatSource *self = (SysprofDiskstatSource *)source;
g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self));
g_assert (writer != NULL);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
self->writer = sysprof_capture_writer_ref (writer);
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->get_is_ready = sysprof_diskstat_source_get_is_ready;
iface->prepare = sysprof_diskstat_source_prepare;
iface->set_writer = sysprof_diskstat_source_set_writer;
iface->start = sysprof_diskstat_source_start;
iface->stop = sysprof_diskstat_source_stop;
}
static void
sysprof_diskstat_source_finalize (GObject *object)
{
SysprofDiskstatSource *self = (SysprofDiskstatSource *)object;
g_clear_pointer (&self->diskstats, g_array_unref);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
if (self->diskstat_fd != -1)
{
close (self->diskstat_fd);
self->diskstat_fd = -1;
}
G_OBJECT_CLASS (sysprof_diskstat_source_parent_class)->finalize (object);
}
static void
sysprof_diskstat_source_class_init (SysprofDiskstatSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_diskstat_source_finalize;
}
static void
sysprof_diskstat_source_init (SysprofDiskstatSource *self)
{
self->diskstats = g_array_new (FALSE, FALSE, sizeof (Diskstat));
}
SysprofSource *
sysprof_diskstat_source_new (void)
{
return g_object_new (SYSPROF_TYPE_DISKSTAT_SOURCE, NULL);
}

View File

@ -1,35 +0,0 @@
/* sysprof-diskstat-source.h
*
* Copyright 2019 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
*/
#pragma once
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DISKSTAT_SOURCE (sysprof_diskstat_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofDiskstatSource, sysprof_diskstat_source, SYSPROF, DISKSTAT_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_diskstat_source_new (void);
G_END_DECLS

View File

@ -1,33 +0,0 @@
/* sysprof-elf-symbol-resolver-private.h
*
* Copyright 2021 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
*/
#pragma once
#include "sysprof-elf-symbol-resolver.h"
G_BEGIN_DECLS
char *_sysprof_elf_symbol_resolver_resolve_path (SysprofElfSymbolResolver *self,
GPid pid,
const char *path);
const char *_sysprof_elf_symbol_resolver_get_pid_kind (SysprofElfSymbolResolver *self,
GPid pid);
G_END_DECLS

View File

@ -1,686 +0,0 @@
/* sysprof-elf-symbol-resolver.c
*
* Copyright 2016-2019 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 <stdio.h>
#include <string.h>
#include "binfile.h"
#include "elfparser.h"
#include "sysprof-elf-symbol-resolver.h"
#include "sysprof-flatpak.h"
#include "sysprof-map-lookaside.h"
#include "sysprof-path-resolver.h"
#include "sysprof-podman.h"
#include "sysprof-symbol-resolver-private.h"
typedef enum
{
PROCESS_KIND_STANDARD,
PROCESS_KIND_FLATPAK,
PROCESS_KIND_PODMAN,
} ProcessKind;
typedef struct
{
char *on_host;
char *in_process;
int layer;
} ProcessOverlay;
typedef struct
{
SysprofMapLookaside *lookaside;
SysprofPathResolver *resolver;
GByteArray *mountinfo_data;
GArray *overlays;
char **debug_dirs;
char *info;
int pid;
guint kind : 2;
} ProcessInfo;
struct _SysprofElfSymbolResolver
{
GObject parent_instance;
GHashTable *processes;
GStringChunk *chunks;
GHashTable *bin_files;
GHashTable *tag_cache;
};
static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface);
G_DEFINE_TYPE_EXTENDED (SysprofElfSymbolResolver,
sysprof_elf_symbol_resolver,
G_TYPE_OBJECT,
0,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER,
symbol_resolver_iface_init))
static void
process_info_free (gpointer data)
{
ProcessInfo *pi = data;
if (pi != NULL)
{
g_clear_pointer (&pi->lookaside, sysprof_map_lookaside_free);
g_clear_pointer (&pi->resolver, _sysprof_path_resolver_free);
g_clear_pointer (&pi->mountinfo_data, g_byte_array_unref);
g_clear_pointer (&pi->overlays, g_array_unref);
g_clear_pointer (&pi->debug_dirs, g_strfreev);
g_clear_pointer (&pi->info, g_free);
g_slice_free (ProcessInfo, pi);
}
}
static const char * const *
process_info_get_debug_dirs (const ProcessInfo *pi)
{
static const char *standard[] = { "/usr/lib/debug", NULL };
if (pi->debug_dirs)
return (const char * const *) pi->debug_dirs;
return standard;
}
static void
sysprof_elf_symbol_resolver_finalize (GObject *object)
{
SysprofElfSymbolResolver *self = (SysprofElfSymbolResolver *)object;
g_clear_pointer (&self->bin_files, g_hash_table_unref);
g_clear_pointer (&self->tag_cache, g_hash_table_unref);
g_clear_pointer (&self->processes, g_hash_table_unref);
g_clear_pointer (&self->chunks, g_string_chunk_free);
G_OBJECT_CLASS (sysprof_elf_symbol_resolver_parent_class)->finalize (object);
}
static void
sysprof_elf_symbol_resolver_class_init (SysprofElfSymbolResolverClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_elf_symbol_resolver_finalize;
}
static void
sysprof_elf_symbol_resolver_init (SysprofElfSymbolResolver *self)
{
self->chunks = g_string_chunk_new (4096);
self->processes = g_hash_table_new_full (NULL, NULL, NULL, process_info_free);
self->bin_files = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
(GDestroyNotify)bin_file_free);
self->tag_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}
static ProcessInfo *
sysprof_elf_symbol_resolver_get_process (SysprofElfSymbolResolver *self,
int pid)
{
ProcessInfo *pi;
g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self));
if (!(pi = g_hash_table_lookup (self->processes, GINT_TO_POINTER (pid))))
{
pi = g_slice_new0 (ProcessInfo);
pi->pid = pid;
g_hash_table_insert (self->processes, GINT_TO_POINTER (pid), pi);
}
return pi;
}
static void
sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver,
SysprofCaptureReader *reader)
{
SysprofElfSymbolResolver *self = (SysprofElfSymbolResolver *)resolver;
static const guint8 zero[1] = {0};
SysprofCaptureFrameType type;
g_autoptr(GByteArray) mounts = NULL;
g_autofree char *mounts_data = NULL;
GHashTableIter iter;
ProcessInfo *pi;
gpointer k, v;
g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self));
g_assert (reader != NULL);
g_hash_table_remove_all (self->processes);
/* First we need to load all the /proc/{pid}/mountinfo files so that
* we can discover what files within the processes filesystem namespace
* were mapped and where. We can use that information later to build
* path resolvers that let us locate the files from the host.
*/
sysprof_capture_reader_reset (reader);
while (sysprof_capture_reader_peek_type (reader, &type))
{
if (type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK)
{
const SysprofCaptureFileChunk *ev;
int out_pid;
if (!(ev = sysprof_capture_reader_read_file (reader)))
break;
pi = sysprof_elf_symbol_resolver_get_process (self, ev->frame.pid);
if (strcmp (ev->path, "/.flatpak-info") == 0)
{
pi->kind = PROCESS_KIND_FLATPAK;
g_free (pi->info);
pi->info = g_strndup ((char *)ev->data, ev->len);
}
else if (strcmp (ev->path, "/run/.containerenv") == 0)
{
pi->kind = PROCESS_KIND_PODMAN;
g_free (pi->info);
pi->info = g_strndup ((char *)ev->data, ev->len);
}
else if (g_str_has_prefix (ev->path, "/proc/") &&
g_str_has_suffix (ev->path, "/mountinfo") &&
sscanf (ev->path, "/proc/%u/mountinfo", &out_pid) == 1)
{
if (pi->mountinfo_data == NULL)
pi->mountinfo_data = g_byte_array_new ();
if (ev->len)
g_byte_array_append (pi->mountinfo_data, ev->data, ev->len);
}
else if (g_str_equal (ev->path, "/proc/mounts"))
{
if (mounts == NULL)
mounts = g_byte_array_new ();
if (ev->len)
g_byte_array_append (mounts, ev->data, ev->len);
}
}
else if (type == SYSPROF_CAPTURE_FRAME_OVERLAY)
{
const SysprofCaptureOverlay *ev;
ProcessOverlay ov;
if (!(ev = sysprof_capture_reader_read_overlay (reader)))
break;
ov.on_host = g_string_chunk_insert_const (self->chunks, ev->data);
ov.in_process = g_string_chunk_insert_const (self->chunks, &ev->data[ev->src_len+1]);
ov.layer = ev->layer;
pi = sysprof_elf_symbol_resolver_get_process (self, ev->frame.pid);
if (pi->overlays == NULL)
pi->overlays = g_array_new (FALSE, FALSE, sizeof (ProcessOverlay));
g_array_append_val (pi->overlays, ov);
}
else
{
if (!sysprof_capture_reader_skip (reader))
break;
}
}
/* Now make sure we have access to /proc/mounts data. If we do not find it
* within the capture, assume we're running on the same host.
*/
if (mounts != NULL)
{
g_byte_array_append (mounts, zero, 1);
mounts_data = (char *)g_byte_array_free (g_steal_pointer (&mounts), FALSE);
}
if (mounts_data == NULL)
g_file_get_contents ("/proc/mounts", &mounts_data, NULL, NULL);
/* Now that we loaded all the mountinfo data, we can create path resolvers
* for each of the processes. Once we have that data we can walk the file
* again to load the map events.
*/
g_hash_table_iter_init (&iter, self->processes);
while (g_hash_table_iter_next (&iter, &k, &v))
{
pi = v;
if (pi->mountinfo_data == NULL)
continue;
g_byte_array_append (pi->mountinfo_data, zero, 1);
pi->resolver = _sysprof_path_resolver_new (mounts_data,
(const char *)pi->mountinfo_data->data);
if (pi->overlays != NULL)
{
for (guint i = 0; i < pi->overlays->len; i++)
{
const ProcessOverlay *ov = &g_array_index (pi->overlays, ProcessOverlay, i);
_sysprof_path_resolver_add_overlay (pi->resolver, ov->in_process, ov->on_host, ov->layer);
}
}
if (pi->kind == PROCESS_KIND_FLATPAK)
{
if (pi->info != NULL)
{
g_autoptr(GKeyFile) keyfile = g_key_file_new ();
if (g_key_file_load_from_data (keyfile, pi->info, (gsize)-1, 0, NULL))
{
if (g_key_file_has_group (keyfile, "Instance"))
{
g_autofree gchar *app_path = g_key_file_get_string (keyfile, "Instance", "app-path", NULL);
g_autofree gchar *runtime_path = g_key_file_get_string (keyfile, "Instance", "runtime-path", NULL);
g_autofree gchar *branch = g_key_file_get_string (keyfile, "Instance", "branch", NULL);
g_autofree gchar *arch = g_key_file_get_string (keyfile, "Instance", "arch", NULL);
g_autofree gchar *app_name = g_key_file_get_string (keyfile, "Application", "name", NULL);
g_autofree gchar *manifest_dir = g_path_get_dirname (app_path);
g_autofree gchar *manifest_path = g_build_filename (manifest_dir, "metadata", NULL);
g_autoptr(GKeyFile) manifest = g_key_file_new ();
GPtrArray *dirs = g_ptr_array_new ();
/* TODO: extensions */
g_ptr_array_add (dirs, g_build_filename (app_path, "lib", "debug", NULL));
g_ptr_array_add (dirs, g_build_filename (runtime_path, "lib", "debug", NULL));
/* Try to figure out flatpak runtime debug symbol paths. */
if (g_key_file_load_from_file (manifest, manifest_path, 0, NULL))
{
/* Add the SDK debug extension. */
g_autofree gchar *sdk = g_key_file_get_string (manifest, "Application", "sdk", NULL);
if (sdk)
{
/* Go from a string like "org.gnome.Sdk/x86_64/41" to "org.gnome.Sdk.Debug/x86_64/41". */
g_autoptr(GString) debug = g_string_new (sdk);
g_string_replace (debug, "/", ".Debug/", 1);
/* Construct a path like "/var/lib/flatpak/runtime/org.gnome.Sdk.Debug/x86_64/41/active/files". */
g_ptr_array_add (dirs, g_build_filename ("/var/lib/flatpak/runtime", debug->str, "active/files", NULL));
}
/* Add the app's debug extension. */
if (app_name && branch && arch)
{
/* Go from a string like "org.gnome.TextEditor" to "org.gnome.TextEditor.Debug". */
g_autoptr(GString) debug = g_string_new (app_name);
g_string_append (debug, ".Debug");
/* Construct a path like "/var/lib/flatpak/runtime/org.gnome.TextEditor.Debug/x86_64/master/active/files". */
g_ptr_array_add (dirs, g_build_filename ("/var/lib/flatpak/runtime", debug->str, arch, branch, "active/files", NULL));
}
}
g_ptr_array_add (dirs, 0);
pi->debug_dirs = (gchar**) g_ptr_array_free (dirs, FALSE);
}
}
}
}
else if (pi->kind == PROCESS_KIND_PODMAN)
{
pi->debug_dirs = g_new0 (gchar *, 2);
pi->debug_dirs[0] = _sysprof_path_resolver_resolve (pi->resolver, "/usr/lib/debug");
pi->debug_dirs[1] = 0;
}
}
/* Walk through the file again and extract maps so long as
* we have a resolver for them already.
*/
sysprof_capture_reader_reset (reader);
while (sysprof_capture_reader_peek_type (reader, &type))
{
if (type == SYSPROF_CAPTURE_FRAME_MAP)
{
const SysprofCaptureMap *ev = sysprof_capture_reader_read_map (reader);
const char *filename = ev->filename;
g_autofree char *resolved = NULL;
SysprofMap map;
pi = sysprof_elf_symbol_resolver_get_process (self, ev->frame.pid);
if (pi->resolver != NULL)
{
resolved = _sysprof_path_resolver_resolve (pi->resolver, filename);
if (resolved)
filename = resolved;
}
map.start = ev->start;
map.end = ev->end;
map.offset = ev->offset;
map.inode = ev->inode;
map.filename = filename;
if (pi->lookaside == NULL)
pi->lookaside = sysprof_map_lookaside_new ();
sysprof_map_lookaside_insert (pi->lookaside, &map);
}
else
{
if (!sysprof_capture_reader_skip (reader))
return;
}
}
}
static bin_file_t *
sysprof_elf_symbol_resolver_get_bin_file (SysprofElfSymbolResolver *self,
const ProcessInfo *pi,
const gchar *filename)
{
g_autofree char *alternate = NULL;
const char * const *debug_dirs;
g_autofree char *on_host = NULL;
bin_file_t *bin_file;
g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self));
if ((bin_file = g_hash_table_lookup (self->bin_files, filename)))
return bin_file;
/* Debug dirs are going to be dependent on the process as different
* containers may affect where the debug symbols are installed.
*/
debug_dirs = process_info_get_debug_dirs (pi);
bin_file = bin_file_new (filename, (const char * const *)debug_dirs);
g_hash_table_insert (self->bin_files, g_strdup (filename), bin_file);
return bin_file;
}
static GQuark
guess_tag (SysprofElfSymbolResolver *self,
const SysprofMap *map)
{
g_assert (map != NULL);
g_assert (map->filename != NULL);
if (!g_hash_table_contains (self->tag_cache, map->filename))
{
GQuark tag = 0;
if (strstr (map->filename, "/libgobject-2.0."))
tag = g_quark_from_static_string ("GObject");
else if (strstr (map->filename, "/libc.so.6"))
tag = g_quark_from_static_string ("libc");
else if (strstr (map->filename, "/libstdc++.so.6"))
tag = g_quark_from_static_string ("stdc++");
else if (strstr (map->filename, "/libglib-2.0."))
tag = g_quark_from_static_string ("GLib");
else if (strstr (map->filename, "/libgio-2.0."))
tag = g_quark_from_static_string ("Gio");
else if (strstr (map->filename, "/libgirepository-1.0."))
tag = g_quark_from_static_string ("Introspection");
else if (strstr (map->filename, "/libgtk-4."))
tag = g_quark_from_static_string ("Gtk 4");
else if (strstr (map->filename, "/libgtk-3."))
tag = g_quark_from_static_string ("Gtk 3");
else if (strstr (map->filename, "/libgdk-3."))
tag = g_quark_from_static_string ("Gdk 3");
else if (strstr (map->filename, "/libgtksourceview-3.0"))
tag = g_quark_from_static_string ("GtkSourceView-3");
else if (strstr (map->filename, "/libgtksourceview-4"))
tag = g_quark_from_static_string ("GtkSourceView-4");
else if (strstr (map->filename, "/libpixman-1"))
tag = g_quark_from_static_string ("Pixman");
else if (strstr (map->filename, "/libcairo."))
tag = g_quark_from_static_string ("cairo");
else if (strstr (map->filename, "/libgstreamer-1."))
tag = g_quark_from_static_string ("GStreamer");
else if (strstr (map->filename, "/libX11."))
tag = g_quark_from_static_string ("X11");
else if (strstr (map->filename, "/libpango-1.0."))
tag = g_quark_from_static_string ("Pango");
else if (strstr (map->filename, "/libpangocairo-1.0."))
tag = g_quark_from_static_string ("Pango");
else if (strstr (map->filename, "/libpangomm-1.4."))
tag = g_quark_from_static_string ("Pango");
else if (strstr (map->filename, "/libpangoft2-1.0"))
tag = g_quark_from_static_string ("Pango");
else if (strstr (map->filename, "/libpangoxft-1.0."))
tag = g_quark_from_static_string ("Pango");
else if (strstr (map->filename, "/libclutter-"))
tag = g_quark_from_static_string ("Clutter");
else if (strstr (map->filename, "/libcogl.") ||
strstr (map->filename, "/libcogl-"))
tag = g_quark_from_static_string ("Cogl");
else if (strstr (map->filename, "/libffi."))
tag = g_quark_from_static_string ("libffi");
else if (strstr (map->filename, "/libwayland-"))
tag = g_quark_from_static_string ("Wayland");
else if (strstr (map->filename, "/libinput."))
tag = g_quark_from_static_string ("libinput");
else if (strstr (map->filename, "/libgjs."))
tag = g_quark_from_static_string ("Gjs");
else if (strstr (map->filename, "/libmozjs-"))
tag = g_quark_from_static_string ("MozJS");
else if (strstr (map->filename, "/libGL."))
tag = g_quark_from_static_string ("GL");
else if (strstr (map->filename, "/libEGL."))
tag = g_quark_from_static_string ("EGL");
g_hash_table_insert (self->tag_cache,
g_strdup (map->filename),
GSIZE_TO_POINTER (tag));
}
return GPOINTER_TO_SIZE (g_hash_table_lookup (self->tag_cache, map->filename));
}
gboolean
sysprof_elf_symbol_resolver_resolve_full (SysprofElfSymbolResolver *self,
guint64 time,
GPid pid,
SysprofAddressContext context,
SysprofCaptureAddress address,
SysprofCaptureAddress *begin,
SysprofCaptureAddress *end,
gchar **name,
GQuark *tag)
{
const bin_symbol_t *bin_sym;
const gchar *bin_sym_name;
const SysprofMap *map;
ProcessInfo *pi;
bin_file_t *bin_file;
gulong ubegin;
gulong uend;
g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self));
g_assert (name != NULL);
g_assert (begin != NULL);
g_assert (end != NULL);
*name = NULL;
if (context != SYSPROF_ADDRESS_CONTEXT_USER)
return FALSE;
if (!(pi = g_hash_table_lookup (self->processes, GINT_TO_POINTER (pid))))
return FALSE;
if (pi->lookaside == NULL)
return FALSE;
if (!(map = sysprof_map_lookaside_lookup (pi->lookaside, address)))
return FALSE;
address -= map->start;
address += map->offset;
bin_file = sysprof_elf_symbol_resolver_get_bin_file (self, pi, map->filename);
g_assert (bin_file != NULL);
/* PERF_RECORD_MMAP doesn't provide an inode, so we can't rely on that
* until we can get PERF_RECORD_MMAP2.
*/
if G_UNLIKELY (map->inode && !bin_file_check_inode (bin_file, map->inode))
{
*name = g_strdup_printf ("%s: inode mismatch", map->filename);
return TRUE;
}
bin_sym = bin_file_lookup_symbol (bin_file, address);
bin_sym_name = bin_symbol_get_name (bin_file, bin_sym);
if G_LIKELY (map->filename)
*tag = guess_tag (self, map);
*name = elf_demangle (bin_sym_name);
bin_symbol_get_address_range (bin_file, bin_sym, &ubegin, &uend);
*begin = ubegin;
*end = uend;
return TRUE;
}
static gchar *
sysprof_elf_symbol_resolver_resolve_with_context (SysprofSymbolResolver *resolver,
guint64 time,
GPid pid,
SysprofAddressContext context,
SysprofCaptureAddress address,
GQuark *tag)
{
gchar *name = NULL;
SysprofCaptureAddress begin, end;
/* If not user context, nothing we can do here */
if (context != SYSPROF_ADDRESS_CONTEXT_USER)
return NULL;
/* If this is a jitmap entry, bail early to save some cycles */
if ((address & SYSPROF_CAPTURE_JITMAP_MARK) == SYSPROF_CAPTURE_JITMAP_MARK)
return NULL;
sysprof_elf_symbol_resolver_resolve_full (SYSPROF_ELF_SYMBOL_RESOLVER (resolver),
time,
pid,
context,
address,
&begin,
&end,
&name,
tag);
return g_steal_pointer (&name);
}
static void
symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface)
{
iface->load = sysprof_elf_symbol_resolver_load;
iface->resolve_with_context = sysprof_elf_symbol_resolver_resolve_with_context;
}
SysprofSymbolResolver *
sysprof_elf_symbol_resolver_new (void)
{
return g_object_new (SYSPROF_TYPE_ELF_SYMBOL_RESOLVER, NULL);
}
void
sysprof_elf_symbol_resolver_add_debug_dir (SysprofElfSymbolResolver *self,
const gchar *debug_dir)
{
/* Do Nothing */
/* XXX: Mark as deprecated post 41 or remove with Gtk4 port */
}
char *
_sysprof_elf_symbol_resolver_resolve_path (SysprofElfSymbolResolver *self,
GPid pid,
const char *path)
{
ProcessInfo *pi;
g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self), NULL);
if (!(pi = g_hash_table_lookup (self->processes, GINT_TO_POINTER (pid))))
return NULL;
if (pi->resolver == NULL)
return NULL;
return _sysprof_path_resolver_resolve (pi->resolver, path);
}
const char *
_sysprof_elf_symbol_resolver_get_pid_kind (SysprofElfSymbolResolver *self,
GPid pid)
{
ProcessInfo *pi;
g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self), NULL);
if (!(pi = g_hash_table_lookup (self->processes, GINT_TO_POINTER (pid))))
return "unknown";
if (pi->kind == PROCESS_KIND_FLATPAK)
return "Flatpak";
if (pi->kind == PROCESS_KIND_PODMAN)
return "Podman";
if (pi->kind == PROCESS_KIND_STANDARD)
return "Standard";
return "unknown";
}

View File

@ -1,53 +0,0 @@
/* sysprof-elf-symbol-resolver.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-symbol-resolver.h"
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_ELF_SYMBOL_RESOLVER (sysprof_elf_symbol_resolver_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofElfSymbolResolver, sysprof_elf_symbol_resolver, SYSPROF, ELF_SYMBOL_RESOLVER, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbolResolver *sysprof_elf_symbol_resolver_new (void);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_elf_symbol_resolver_add_debug_dir (SysprofElfSymbolResolver *self,
const gchar *debug_dir);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_elf_symbol_resolver_resolve_full (SysprofElfSymbolResolver *self,
guint64 time,
GPid pid,
SysprofAddressContext context,
SysprofCaptureAddress address,
SysprofCaptureAddress *begin,
SysprofCaptureAddress *end,
gchar **name,
GQuark *tag);
G_END_DECLS

View File

@ -1,158 +0,0 @@
/* sysprof-flatpak.c
*
* Copyright 2019 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 <sys/utsname.h>
#include "sysprof-flatpak.h"
#define ETC_INSTALLATIONS_D "/etc/flatpak/installations.d"
static void
add_from_installations_d (GPtrArray *ret,
const gchar *path,
const gchar *prefix)
{
g_autoptr(GDir) dir = NULL;
g_assert (ret != NULL);
g_assert (path != NULL);
/* Now look at /etc/flatpak/installations.d for keyfiles with Path= */
if ((dir = g_dir_open (path, 0, NULL)))
{
const gchar *name;
while ((name = g_dir_read_name (dir)))
{
g_autofree gchar *key_path = g_build_filename (path, name, NULL);
g_autoptr(GKeyFile) kf = g_key_file_new ();
if (g_key_file_load_from_file (kf, key_path, G_KEY_FILE_NONE, NULL))
{
g_auto(GStrv) groups = g_key_file_get_groups (kf, NULL);
for (guint i = 0; groups[i]; i++)
{
if (g_key_file_has_key (kf, groups[i], "Path", NULL))
{
gchar *val = g_key_file_get_string (kf, groups[i], "Path", NULL);
if (val)
{
if (prefix)
g_ptr_array_add (ret, g_build_filename (prefix, val, NULL));
else
g_ptr_array_add (ret, g_steal_pointer (&val));
}
}
}
}
}
}
}
gchar **
get_installations (void)
{
GPtrArray *ret = g_ptr_array_new ();
/* We might be running from a container, so ignore XDG_DATA_HOME as
* that will likely be different that what we care about the host.
* TODO: Can we find a way to support non-standard XDG_DATA_HOME?
*/
g_ptr_array_add (ret, g_build_filename (g_get_home_dir (), ".local", "share", "flatpak", NULL));
g_ptr_array_add (ret, g_strdup ("/var/lib/flatpak"));
add_from_installations_d (ret, ETC_INSTALLATIONS_D, NULL);
add_from_installations_d (ret, "/var/run/host" ETC_INSTALLATIONS_D, "/var/run/host");
g_ptr_array_add (ret, NULL);
return (gchar **)g_ptr_array_free (ret, FALSE);
}
static void
get_arch (gchar *out,
gsize len)
{
struct utsname u;
uname (&u);
g_strlcpy (out, u.machine, len);
}
void
_sysprof_flatpak_debug_dirs (GPtrArray *dirs)
{
g_auto(GStrv) installs = get_installations ();
gchar arch[32];
g_assert (dirs != NULL);
get_arch (arch, sizeof arch);
g_ptr_array_add (dirs, g_strdup ("/var/run/host/usr/lib/debug"));
g_ptr_array_add (dirs, g_strdup ("/var/run/host/usr/lib32/debug"));
g_ptr_array_add (dirs, g_strdup ("/var/run/host/usr/lib64/debug"));
/* For each of the installations, we want to look at all of the runtimes that
* exist within it. Of those runtimes, we want to limit ourselves to the active
* version of each runtime, and see if we have a deployment for the current
* system arch that contains a "lib/debug" directory. We could add more, but
* it's just too many directories.
*/
for (guint i = 0; installs[i]; i++)
{
g_autofree gchar *repo_dir = g_build_filename (installs[i], "runtime", NULL);
g_autoptr(GDir) dir = g_dir_open (repo_dir, 0, NULL);
const gchar *name;
if (dir == NULL)
continue;
while ((name = g_dir_read_name (dir)))
{
g_autofree gchar *version_dir = g_build_filename (installs[i], "runtime", name, arch, NULL);
g_autoptr(GDir) vdir = g_dir_open (version_dir, 0, NULL);
const gchar *version;
if (vdir == NULL)
continue;
while ((version = g_dir_read_name (vdir)))
{
g_autofree gchar *lib_debug = g_build_filename (version_dir, version, "active", "files", "lib", "debug", NULL);
if (g_file_test (lib_debug, G_FILE_TEST_EXISTS))
g_ptr_array_add (dirs, g_steal_pointer (&lib_debug));
}
}
}
}
gchar **
sysprof_flatpak_debug_dirs (void)
{
GPtrArray *dirs = g_ptr_array_new ();
_sysprof_flatpak_debug_dirs (dirs);
g_ptr_array_add (dirs, NULL);
return (gchar **)g_ptr_array_free (dirs, FALSE);
}

View File

@ -1,29 +0,0 @@
/* sysprof-flatpak.h
*
* Copyright 2019 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
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
gchar **sysprof_flatpak_debug_dirs (void);
G_END_DECLS

View File

@ -1,68 +0,0 @@
/* sysprof-gjs-source.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-gjs-source"
#include "config.h"
#include "sysprof-gjs-source.h"
struct _SysprofGjsSource
{
SysprofTracefdSource parent_instance;
};
static SysprofSourceInterface *parent_iface;
static void
sysprof_gjs_source_modify_spawn (SysprofSource *source,
SysprofSpawnable *spawnable)
{
sysprof_spawnable_setenv (spawnable, "GJS_ENABLE_PROFILER", "1");
parent_iface->modify_spawn (source, spawnable);
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
parent_iface = g_type_interface_peek_parent (iface);
iface->modify_spawn = sysprof_gjs_source_modify_spawn;
}
G_DEFINE_TYPE_WITH_CODE (SysprofGjsSource, sysprof_gjs_source, SYSPROF_TYPE_TRACEFD_SOURCE,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static void
sysprof_gjs_source_class_init (SysprofGjsSourceClass *klass)
{
}
static void
sysprof_gjs_source_init (SysprofGjsSource *self)
{
sysprof_tracefd_source_set_envvar (SYSPROF_TRACEFD_SOURCE (self), "GJS_TRACE_FD");
}
SysprofSource *
sysprof_gjs_source_new (void)
{
return g_object_new (SYSPROF_TYPE_GJS_SOURCE, NULL);
}

View File

@ -1,35 +0,0 @@
/* sysprof-gjs-source.h
*
* Copyright 2019 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
*/
#pragma once
#include "sysprof-tracefd-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_GJS_SOURCE (sysprof_gjs_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofGjsSource, sysprof_gjs_source, SYSPROF, GJS_SOURCE, SysprofTracefdSource)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_gjs_source_new (void);
G_END_DECLS

View File

@ -1,318 +0,0 @@
/* sysprof-governor-source.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-governor-source"
#include "config.h"
#include "sysprof-governor-source.h"
#include "sysprof-helpers.h"
struct _SysprofGovernorSource
{
GObject parent_instance;
gchar *old_governor;
int old_paranoid;
guint disable_governor : 1;
};
enum {
PROP_0,
PROP_DISABLE_GOVERNOR,
N_PROPS
};
static void source_iface_init (SysprofSourceInterface *iface);
G_DEFINE_TYPE_WITH_CODE (SysprofGovernorSource, sysprof_governor_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static GParamSpec *properties [N_PROPS];
static void
sysprof_governor_source_finalize (GObject *object)
{
SysprofGovernorSource *self = (SysprofGovernorSource *)object;
g_clear_pointer (&self->old_governor, g_free);
G_OBJECT_CLASS (sysprof_governor_source_parent_class)->finalize (object);
}
static void
sysprof_governor_source_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofGovernorSource *self = SYSPROF_GOVERNOR_SOURCE (object);
switch (prop_id)
{
case PROP_DISABLE_GOVERNOR:
g_value_set_boolean (value, sysprof_governor_source_get_disable_governor (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_governor_source_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofGovernorSource *self = SYSPROF_GOVERNOR_SOURCE (object);
switch (prop_id)
{
case PROP_DISABLE_GOVERNOR:
sysprof_governor_source_set_disable_governor (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_governor_source_class_init (SysprofGovernorSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_governor_source_finalize;
object_class->get_property = sysprof_governor_source_get_property;
object_class->set_property = sysprof_governor_source_set_property;
properties [PROP_DISABLE_GOVERNOR] =
g_param_spec_boolean ("disable-governor",
"Disable Governor",
"Disable Governor",
TRUE,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_governor_source_init (SysprofGovernorSource *self)
{
self->disable_governor = FALSE;
self->old_paranoid = 2;
}
SysprofSource *
sysprof_governor_source_new (void)
{
return g_object_new (SYSPROF_TYPE_GOVERNOR_SOURCE, NULL);
}
gboolean
sysprof_governor_source_get_disable_governor (SysprofGovernorSource *self)
{
g_return_val_if_fail (SYSPROF_IS_GOVERNOR_SOURCE (self), FALSE);
return self->disable_governor;
}
void
sysprof_governor_source_set_disable_governor (SysprofGovernorSource *self,
gboolean disable_governor)
{
g_return_if_fail (SYSPROF_IS_GOVERNOR_SOURCE (self));
disable_governor = !!disable_governor;
if (disable_governor != self->disable_governor)
{
self->disable_governor = disable_governor;
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DISABLE_GOVERNOR]);
}
}
static void
sysprof_governor_source_serialize (SysprofSource *source,
GKeyFile *keyfile,
const gchar *group)
{
SysprofGovernorSource *self = (SysprofGovernorSource *)source;
g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self));
g_assert (keyfile != NULL);
g_assert (group != NULL);
g_key_file_set_boolean (keyfile, group, "disable-governor", self->disable_governor);
}
static void
sysprof_governor_source_deserialize (SysprofSource *source,
GKeyFile *keyfile,
const gchar *group)
{
SysprofGovernorSource *self = (SysprofGovernorSource *)source;
g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self));
g_assert (keyfile != NULL);
g_assert (group != NULL);
sysprof_governor_source_set_disable_governor (self,
g_key_file_get_boolean (keyfile, group, "disable-governor", NULL));
}
static void
disable_governor_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofHelpers *helpers = (SysprofHelpers *)object;
g_autoptr(SysprofGovernorSource) self = user_data;
g_autoptr(GError) error = NULL;
g_autofree gchar *old_governor = NULL;
g_assert (SYSPROF_IS_HELPERS (helpers));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self));
if (!sysprof_helpers_set_governor_finish (helpers, result, &old_governor, &error))
g_warning ("Failed to change governor: %s", error->message);
else
self->old_governor = g_steal_pointer (&old_governor);
sysprof_source_emit_ready (SYSPROF_SOURCE (self));
}
static void
disable_paranoid_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofHelpers *helpers = (SysprofHelpers *)object;
g_autoptr(SysprofGovernorSource) self = user_data;
g_autoptr(GError) error = NULL;
int old_paranoid;
g_assert (SYSPROF_IS_HELPERS (helpers));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self));
if (!sysprof_helpers_set_paranoid_finish (helpers, result, &old_paranoid, &error))
g_debug ("Failed to change perf_event_paranoid: %s", error->message);
else
self->old_paranoid = old_paranoid;
if (!self->disable_governor)
sysprof_source_emit_ready (SYSPROF_SOURCE (self));
else
sysprof_helpers_set_governor_async (helpers,
"performance",
NULL,
disable_governor_cb,
g_steal_pointer (&self));
}
static void
sysprof_governor_source_prepare (SysprofSource *source)
{
SysprofGovernorSource *self = (SysprofGovernorSource *)source;
SysprofHelpers *helpers = sysprof_helpers_get_default ();
g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self));
sysprof_helpers_set_paranoid_async (helpers,
-1,
NULL,
disable_paranoid_cb,
g_object_ref (self));
}
static void
enable_governor_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofHelpers *helpers = (SysprofHelpers *)object;
g_autoptr(SysprofGovernorSource) self = user_data;
g_autoptr(GError) error = NULL;
g_autofree gchar *old_governor = NULL;
g_assert (SYSPROF_IS_HELPERS (helpers));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self));
if (!sysprof_helpers_set_governor_finish (helpers, result, &old_governor, &error))
g_warning ("Failed to change governor: %s", error->message);
g_clear_pointer (&self->old_governor, g_free);
sysprof_source_emit_finished (SYSPROF_SOURCE (self));
}
static void
enable_paranoid_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofHelpers *helpers = (SysprofHelpers *)object;
g_autoptr(SysprofGovernorSource) self = user_data;
g_autoptr(GError) error = NULL;
int old_governor;
g_assert (SYSPROF_IS_HELPERS (helpers));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self));
if (!sysprof_helpers_set_paranoid_finish (helpers, result, &old_governor, &error))
g_debug ("Failed to change event_perf_paranoid: %s", error->message);
if (!self->disable_governor || self->old_governor == NULL)
sysprof_source_emit_finished (SYSPROF_SOURCE (self));
else
sysprof_helpers_set_governor_async (helpers,
self->old_governor,
NULL,
enable_governor_cb,
g_object_ref (self));
}
static void
sysprof_governor_source_stop (SysprofSource *source)
{
SysprofGovernorSource *self = (SysprofGovernorSource *)source;
SysprofHelpers *helpers = sysprof_helpers_get_default ();
g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self));
sysprof_helpers_set_paranoid_async (helpers,
self->old_paranoid,
NULL,
enable_paranoid_cb,
g_object_ref (self));
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->deserialize = sysprof_governor_source_deserialize;
iface->prepare = sysprof_governor_source_prepare;
iface->serialize = sysprof_governor_source_serialize;
iface->stop = sysprof_governor_source_stop;
}

View File

@ -1,40 +0,0 @@
/* sysprof-governor-source.h
*
* Copyright 2019 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
*/
#pragma once
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_GOVERNOR_SOURCE (sysprof_governor_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofGovernorSource, sysprof_governor_source, SYSPROF, GOVERNOR_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_governor_source_new (void);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_governor_source_get_disable_governor (SysprofGovernorSource *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_governor_source_set_disable_governor (SysprofGovernorSource *self,
gboolean disable_governor);
G_END_DECLS

View File

@ -1,835 +0,0 @@
/* sysprof-helpers.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-helpers"
#include "config.h"
#include <gio/gunixfdlist.h>
#include "sysprof-helpers.h"
#include "sysprof-polkit-private.h"
#include "helpers.h"
#include "ipc-service.h"
struct _SysprofHelpers
{
GObject parent_instance;
IpcService *proxy;
GQueue auth_tasks;
guint did_auth : 1;
};
G_DEFINE_TYPE (SysprofHelpers, sysprof_helpers, G_TYPE_OBJECT)
static void
sysprof_helpers_finalize (GObject *object)
{
SysprofHelpers *self = (SysprofHelpers *)object;
g_clear_object (&self->proxy);
G_OBJECT_CLASS (sysprof_helpers_parent_class)->finalize (object);
}
static void
sysprof_helpers_class_init (SysprofHelpersClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_helpers_finalize;
}
static void
sysprof_helpers_init (SysprofHelpers *self)
{
g_autoptr(GDBusConnection) bus = NULL;
if ((bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL)))
self->proxy = ipc_service_proxy_new_sync (bus,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION,
"org.gnome.Sysprof3",
"/org/gnome/Sysprof3",
NULL, NULL);
}
SysprofHelpers *
sysprof_helpers_get_default (void)
{
static SysprofHelpers *instance;
if (g_once_init_enter (&instance))
{
SysprofHelpers *self = g_object_new (SYSPROF_TYPE_HELPERS, NULL);
g_object_add_weak_pointer (G_OBJECT (self), (gpointer *)&instance);
g_once_init_leave (&instance, self);
}
return instance;
}
static gboolean
fail_if_no_proxy (SysprofHelpers *self,
GTask *task)
{
g_assert (SYSPROF_IS_HELPERS (self));
g_assert (G_IS_TASK (task));
if (self->proxy == NULL)
{
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_NOT_CONNECTED,
"No D-Bus proxy to communicate with daemon");
return TRUE;
}
return FALSE;
}
static void
sysprof_helpers_list_processes_local_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
g_autoptr(GError) error = NULL;
g_autofree gint32 *processes = NULL;
gsize n_processes = 0;
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (helpers_list_processes_finish (result, &processes, &n_processes, &error))
{
g_autoptr(GVariant) ret = NULL;
ret = g_variant_new_fixed_array (G_VARIANT_TYPE_INT32,
processes,
n_processes,
sizeof (gint32));
g_task_return_pointer (task,
g_variant_take_ref (g_steal_pointer (&ret)),
(GDestroyNotify) g_variant_unref);
return;
}
g_task_return_error (task, g_steal_pointer (&error));
}
static void
sysprof_helpers_list_processes_cb (IpcService *service,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
g_autoptr(GVariant) processes = NULL;
g_autoptr(GError) error = NULL;
g_assert (IPC_IS_SERVICE (service));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (!ipc_service_call_list_processes_finish (service, &processes, result, &error))
helpers_list_processes_async (g_task_get_cancellable (task),
sysprof_helpers_list_processes_local_cb,
g_object_ref (task));
else
g_task_return_pointer (task, g_steal_pointer (&processes), (GDestroyNotify) g_variant_unref);
}
gboolean
sysprof_helpers_list_processes (SysprofHelpers *self,
GCancellable *cancellable,
gint32 **processes,
gsize *n_processes,
GError **error)
{
g_autoptr(GVariant) fixed_ar = NULL;
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (processes != NULL, FALSE);
g_return_val_if_fail (n_processes != NULL, FALSE);
if (helpers_can_see_pids ())
{
/* No need to query remote if we can see pids in this namespace */
if (helpers_list_processes (processes, n_processes))
return TRUE;
}
if (self->proxy && ipc_service_call_list_processes_sync (self->proxy, &fixed_ar, cancellable, NULL))
{
const gint32 *data;
gsize len;
data = g_variant_get_fixed_array (fixed_ar, &len, sizeof (gint32));
*processes = g_memdup2 (data, len * sizeof (gint32));
*n_processes = len;
return TRUE;
}
helpers_list_processes (processes, n_processes);
return TRUE;
}
void
sysprof_helpers_list_processes_async (SysprofHelpers *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SYSPROF_IS_HELPERS (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_helpers_list_processes_async);
if (self->proxy != NULL)
ipc_service_call_list_processes (self->proxy,
cancellable,
(GAsyncReadyCallback) sysprof_helpers_list_processes_cb,
g_steal_pointer (&task));
else
helpers_list_processes_async (cancellable,
sysprof_helpers_list_processes_local_cb,
g_steal_pointer (&task));
}
gboolean
sysprof_helpers_list_processes_finish (SysprofHelpers *self,
GAsyncResult *result,
gint32 **processes,
gsize *n_processes,
GError **error)
{
g_autoptr(GVariant) ret = NULL;
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (G_IS_TASK (result), FALSE);
if ((ret = g_task_propagate_pointer (G_TASK (result), error)))
{
const gint32 *p;
gsize n;
p = g_variant_get_fixed_array (ret, &n, sizeof (gint32));
if (processes != NULL)
*processes = g_memdup2 (p, n * sizeof (gint32));
if (n_processes != NULL)
*n_processes = n;
return TRUE;
}
return FALSE;
}
gboolean
sysprof_helpers_get_proc_fd (SysprofHelpers *self,
const gchar *path,
GCancellable *cancellable,
gint *out_fd,
GError **error)
{
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (path != NULL, FALSE);
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (out_fd != NULL, FALSE);
*out_fd = -1;
if (self->proxy != NULL)
{
g_autoptr(GVariant) reply = NULL;
g_autoptr(GUnixFDList) out_fd_list = NULL;
reply = g_dbus_proxy_call_with_unix_fd_list_sync (G_DBUS_PROXY (self->proxy),
"GetProcFd",
g_variant_new ("(^ay)", path),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
NULL,
&out_fd_list,
cancellable,
error);
if (reply != NULL && out_fd_list != NULL)
{
gint handle = -1;
g_variant_get (reply, "(h)", &handle);
if (handle < g_unix_fd_list_get_length (out_fd_list))
{
*out_fd = g_unix_fd_list_get (out_fd_list, handle, error);
return *out_fd != -1;
}
}
}
if (!helpers_get_proc_fd (path, out_fd))
return FALSE;
g_clear_error (error);
return TRUE;
}
static void
sysprof_helpers_get_proc_file_cb (IpcService *service,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
g_autoptr(GError) error = NULL;
g_autofree gchar *contents = NULL;
g_assert (IPC_IS_SERVICE (service));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (!ipc_service_call_get_proc_file_finish (service, &contents, result, &error))
{
const gchar *path = g_task_get_task_data (task);
gsize len;
if (!helpers_get_proc_file (path, &contents, &len))
{
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_clear_error (&error);
}
g_task_return_pointer (task, g_steal_pointer (&contents), g_free);
}
gboolean
sysprof_helpers_get_proc_file (SysprofHelpers *self,
const gchar *path,
GCancellable *cancellable,
gchar **contents,
GError **error)
{
gsize len;
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
if (self->proxy != NULL)
{
if (ipc_service_call_get_proc_file_sync (self->proxy, path, contents, cancellable, error))
return TRUE;
}
if (!helpers_get_proc_file (path, contents, &len))
return FALSE;
if (error != NULL)
g_clear_error (error);
return TRUE;
}
void
sysprof_helpers_get_proc_file_async (SysprofHelpers *self,
const gchar *path,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SYSPROF_IS_HELPERS (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_helpers_get_proc_file_async);
g_task_set_task_data (task, g_strdup (path), g_free);
if (!fail_if_no_proxy (self, task))
ipc_service_call_get_proc_file (self->proxy,
path,
cancellable,
(GAsyncReadyCallback) sysprof_helpers_get_proc_file_cb,
g_steal_pointer (&task));
}
gboolean
sysprof_helpers_get_proc_file_finish (SysprofHelpers *self,
GAsyncResult *result,
gchar **contents,
GError **error)
{
g_autofree gchar *ret = NULL;
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (G_IS_TASK (result), FALSE);
if ((ret = g_task_propagate_pointer (G_TASK (result), error)))
{
if (contents != NULL)
*contents = g_steal_pointer (&ret);
return TRUE;
}
return FALSE;
}
#ifdef __linux__
static GVariant *
build_options_dict (struct perf_event_attr *attr)
{
return g_variant_take_ref (
g_variant_new_parsed ("["
"{'comm', <%b>},"
#ifdef HAVE_PERF_CLOCKID
"{'clockid', <%i>},"
"{'use_clockid', <%b>},"
#endif
"{'config', <%t>},"
"{'disabled', <%b>},"
"{'exclude_idle', <%b>},"
"{'mmap', <%b>},"
"{'wakeup_events', <%u>},"
"{'sample_id_all', <%b>},"
"{'sample_period', <%t>},"
"{'sample_type', <%t>},"
"{'task', <%b>},"
"{'type', <%u>}"
"]",
(gboolean)!!attr->comm,
#ifdef HAVE_PERF_CLOCKID
(gint32)attr->clockid,
(gboolean)!!attr->use_clockid,
#endif
(guint64)attr->config,
(gboolean)!!attr->disabled,
(gboolean)!!attr->exclude_idle,
(gboolean)!!attr->mmap,
(guint32)attr->wakeup_events,
(gboolean)!!attr->sample_id_all,
(guint64)attr->sample_period,
(guint64)attr->sample_type,
(gboolean)!!attr->task,
(guint32)attr->type));
}
gboolean
sysprof_helpers_perf_event_open (SysprofHelpers *self,
struct perf_event_attr *attr,
gint32 pid,
gint32 cpu,
gint32 group_fd,
guint64 flags,
GCancellable *cancellable,
gint *out_fd,
GError **error)
{
g_autoptr(GUnixFDList) fd_list = NULL;
g_autoptr(GUnixFDList) out_fd_list = NULL;
g_autoptr(GVariant) options = NULL;
g_autoptr(GVariant) reply = NULL;
gint handle = -1;
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (group_fd >= -1, FALSE);
g_return_val_if_fail (out_fd != NULL, FALSE);
*out_fd = -1;
if (self->proxy == NULL)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_CONNECTED,
"No access to system proxy");
return FALSE;
}
if (group_fd != -1)
{
fd_list = g_unix_fd_list_new ();
handle = g_unix_fd_list_append (fd_list, group_fd, NULL);
}
options = build_options_dict (attr);
reply = g_dbus_proxy_call_with_unix_fd_list_sync (G_DBUS_PROXY (self->proxy),
"PerfEventOpen",
g_variant_new ("(@a{sv}iiht)",
options,
pid,
cpu,
handle,
flags),
G_DBUS_CALL_FLAGS_NONE,
-1,
fd_list,
&out_fd_list,
cancellable,
error);
if (reply == NULL)
{
/* Try in-process (without elevated privs) */
if (helpers_perf_event_open (options, pid, cpu, group_fd, flags, out_fd))
{
g_clear_error (error);
return TRUE;
}
return FALSE;
}
if (out_fd_list == NULL || g_unix_fd_list_get_length (out_fd_list) != 1)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Received invalid reply from peer");
return FALSE;
}
*out_fd = g_unix_fd_list_get (out_fd_list, 0, error);
return *out_fd != -1;
}
#endif /* __linux__ */
static void
sysprof_helpers_authorize_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(SysprofHelpers) self = user_data;
g_autoptr(GError) error = NULL;
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_HELPERS (self));
if (!_sysprof_polkit_authorize_for_bus_finish (result, &error))
{
while (self->auth_tasks.length > 0)
{
g_autoptr(GTask) task = g_queue_pop_head (&self->auth_tasks);
g_task_return_error (task, g_error_copy (error));
}
}
else
{
self->did_auth = TRUE;
while (self->auth_tasks.length > 0)
{
g_autoptr(GTask) task = g_queue_pop_head (&self->auth_tasks);
g_task_return_boolean (task, TRUE);
}
}
}
static void
sysprof_helpers_do_auth (SysprofHelpers *self)
{
GDBusConnection *bus;
g_assert (SYSPROF_IS_HELPERS (self));
if (self->proxy == NULL || self->did_auth)
{
/* No D-Bus/Polkit? Bail early, fail sooner. If we already successfully
* did auth, then short circuit to avoid spamming the user.
*/
while (self->auth_tasks.length > 0)
{
g_autoptr(GTask) task = g_queue_pop_head (&self->auth_tasks);
g_task_return_boolean (task, TRUE);
}
return;
}
bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (self->proxy));
_sysprof_polkit_authorize_for_bus_async (bus,
"org.gnome.sysprof3.profile",
NULL,
TRUE,
NULL,
sysprof_helpers_authorize_cb,
g_object_ref (self));
}
void
sysprof_helpers_authorize_async (SysprofHelpers *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SYSPROF_IS_HELPERS (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_helpers_authorize_async);
g_queue_push_tail (&self->auth_tasks, g_steal_pointer (&task));
if (self->auth_tasks.length == 1)
sysprof_helpers_do_auth (self);
}
gboolean
sysprof_helpers_authorize_finish (SysprofHelpers *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (G_IS_TASK (result), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
gboolean
sysprof_helpers_get_process_info (SysprofHelpers *self,
const gchar *attributes,
gboolean no_proxy,
GCancellable *cancellable,
GVariant **info,
GError **error)
{
g_assert (SYSPROF_IS_HELPERS (self));
g_assert (attributes != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
g_assert (info != NULL);
if (no_proxy)
{
*info = helpers_get_process_info (attributes);
return TRUE;
}
return ipc_service_call_get_process_info_sync (self->proxy, attributes, info, cancellable, error);
}
static void
sysprof_helpers_get_process_info_cb (IpcService *service,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
g_autoptr(GVariant) info = NULL;
g_autoptr(GError) error = NULL;
g_assert (IPC_IS_SERVICE (service));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (!ipc_service_call_get_process_info_finish (service, &info, result, &error))
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_return_pointer (task, g_steal_pointer (&info), (GDestroyNotify)g_variant_unref);
}
void
sysprof_helpers_get_process_info_async (SysprofHelpers *self,
const gchar *attributes,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_assert (SYSPROF_IS_HELPERS (self));
g_assert (attributes != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_helpers_get_process_info_async);
ipc_service_call_get_process_info (self->proxy,
attributes,
cancellable,
(GAsyncReadyCallback) sysprof_helpers_get_process_info_cb,
g_steal_pointer (&task));
}
gboolean
sysprof_helpers_get_process_info_finish (SysprofHelpers *self,
GAsyncResult *result,
GVariant **info,
GError **error)
{
g_autoptr(GVariant) ret = NULL;
g_assert (SYSPROF_IS_HELPERS (self));
g_assert (G_IS_TASK (result));
if ((ret = g_task_propagate_pointer (G_TASK (result), error)))
{
if (info)
*info = g_steal_pointer (&ret);
return TRUE;
}
return FALSE;
}
static void
sysprof_helpers_set_governor_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
IpcService *proxy = (IpcService *)object;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
gchar *old_governor = NULL;
g_assert (IPC_IS_SERVICE (proxy));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (!ipc_service_call_set_governor_finish (proxy, &old_governor, result, &error))
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_return_pointer (task, old_governor, g_free);
}
void
sysprof_helpers_set_governor_async (SysprofHelpers *self,
const gchar *governor,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SYSPROF_IS_HELPERS (self));
g_return_if_fail (governor != NULL);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_helpers_set_governor_async);
if (fail_if_no_proxy (self, task))
return;
ipc_service_call_set_governor (self->proxy,
governor,
cancellable,
sysprof_helpers_set_governor_cb,
g_steal_pointer (&task));
}
gboolean
sysprof_helpers_set_governor_finish (SysprofHelpers *self,
GAsyncResult *result,
gchar **old_governor,
GError **error)
{
g_autofree gchar *ret = NULL;
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (G_IS_TASK (result), FALSE);
if ((ret = g_task_propagate_pointer (G_TASK (result), error)))
{
if (old_governor != NULL)
*old_governor = g_steal_pointer (&ret);
return TRUE;
}
return FALSE;
}
static void
sysprof_helpers_set_paranoid_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
IpcService *proxy = (IpcService *)object;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
int old_paranoid = G_MAXINT;
g_assert (IPC_IS_SERVICE (proxy));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (!ipc_service_call_set_paranoid_finish (proxy, &old_paranoid, result, &error))
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_return_int (task, old_paranoid);
}
void
sysprof_helpers_set_paranoid_async (SysprofHelpers *self,
int paranoid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SYSPROF_IS_HELPERS (self));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_helpers_set_paranoid_async);
if (fail_if_no_proxy (self, task))
return;
ipc_service_call_set_paranoid (self->proxy,
paranoid,
cancellable,
sysprof_helpers_set_paranoid_cb,
g_steal_pointer (&task));
}
gboolean
sysprof_helpers_set_paranoid_finish (SysprofHelpers *self,
GAsyncResult *result,
int *old_paranoid,
GError **error)
{
g_autoptr(GError) local_error = NULL;
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
g_return_val_if_fail (G_IS_TASK (result), FALSE);
*old_paranoid = g_task_propagate_int (G_TASK (result), &local_error);
if (local_error)
{
g_propagate_error (error, g_steal_pointer (&local_error));
return FALSE;
}
return TRUE;
}

View File

@ -1,121 +0,0 @@
/* sysprof-helpers.h
*
* Copyright 2019 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
*/
#pragma once
#include <gio/gio.h>
#ifdef __linux__
# include <linux/perf_event.h>
#endif
G_BEGIN_DECLS
#define SYSPROF_TYPE_HELPERS (sysprof_helpers_get_type())
G_DECLARE_FINAL_TYPE (SysprofHelpers, sysprof_helpers, SYSPROF, HELPERS, GObject)
SysprofHelpers *sysprof_helpers_get_default (void);
void sysprof_helpers_authorize_async (SysprofHelpers *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean sysprof_helpers_authorize_finish (SysprofHelpers *self,
GAsyncResult *result,
GError **error);
gboolean sysprof_helpers_list_processes (SysprofHelpers *self,
GCancellable *cancellable,
gint32 **processes,
gsize *n_processes,
GError **error);
void sysprof_helpers_list_processes_async (SysprofHelpers *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean sysprof_helpers_list_processes_finish (SysprofHelpers *self,
GAsyncResult *result,
gint32 **processes,
gsize *n_processes,
GError **error);
gboolean sysprof_helpers_get_proc_fd (SysprofHelpers *self,
const gchar *path,
GCancellable *cancellable,
gint *out_fd,
GError **error);
gboolean sysprof_helpers_get_proc_file (SysprofHelpers *self,
const gchar *path,
GCancellable *cancellable,
gchar **contents,
GError **error);
void sysprof_helpers_get_proc_file_async (SysprofHelpers *self,
const gchar *path,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean sysprof_helpers_get_proc_file_finish (SysprofHelpers *self,
GAsyncResult *result,
gchar **contents,
GError **error);
gboolean sysprof_helpers_get_process_info (SysprofHelpers *self,
const gchar *attributes,
gboolean no_proxy,
GCancellable *cancellable,
GVariant **info,
GError **error);
void sysprof_helpers_get_process_info_async (SysprofHelpers *self,
const gchar *attributes,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean sysprof_helpers_get_process_info_finish(SysprofHelpers *self,
GAsyncResult *result,
GVariant **info,
GError **error);
#ifdef __linux__
gboolean sysprof_helpers_perf_event_open (SysprofHelpers *self,
struct perf_event_attr *attr,
gint32 pid,
gint32 cpu,
gint32 group_fd,
guint64 flags,
GCancellable *cancellable,
gint *out_fd,
GError **error);
#endif
void sysprof_helpers_set_governor_async (SysprofHelpers *self,
const gchar *governor,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean sysprof_helpers_set_governor_finish (SysprofHelpers *self,
GAsyncResult *result,
gchar **old_governor,
GError **error);
void sysprof_helpers_set_paranoid_async (SysprofHelpers *self,
int paranoid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean sysprof_helpers_set_paranoid_finish (SysprofHelpers *self,
GAsyncResult *result,
int *old_paranoid,
GError **error);
G_END_DECLS

View File

@ -1,496 +0,0 @@
/* sysprof-hostinfo-source.c
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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 <ctype.h>
#include <fcntl.h>
#include <glib/gstdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "sysprof-helpers.h"
#include "sysprof-hostinfo-source.h"
#define PROC_STAT_BUF_SIZE 4096
struct _SysprofHostinfoSource
{
GObject parent_instance;
guint handler;
gint n_cpu;
gint stat_fd;
guint combined_id;
GArray *freqs;
SysprofCaptureWriter *writer;
GArray *cpu_info;
gchar *stat_buf;
};
typedef struct
{
gint counter_base;
gdouble total;
glong last_user;
glong last_idle;
glong last_system;
glong last_nice;
glong last_iowait;
glong last_irq;
glong last_softirq;
glong last_steal;
glong last_guest;
glong last_guest_nice;
} CpuInfo;
typedef struct
{
gint stat_fd;
gint64 max;
} CpuFreq;
static void source_iface_init (SysprofSourceInterface *iface);
G_DEFINE_TYPE_EXTENDED (SysprofHostinfoSource, sysprof_hostinfo_source, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
SysprofSource *
sysprof_hostinfo_source_new (void)
{
return g_object_new (SYSPROF_TYPE_HOSTINFO_SOURCE, NULL);
}
static gboolean
read_stat (SysprofHostinfoSource *self)
{
gssize len;
g_assert (self != NULL);
g_assert (self->stat_fd != -1);
g_assert (self->stat_buf != NULL);
if (lseek (self->stat_fd, 0, SEEK_SET) != 0)
return FALSE;
len = read (self->stat_fd, self->stat_buf, PROC_STAT_BUF_SIZE);
if (len <= 0)
return FALSE;
if (len < PROC_STAT_BUF_SIZE)
self->stat_buf[len] = 0;
else
self->stat_buf[PROC_STAT_BUF_SIZE-1] = 0;
return TRUE;
}
static void
poll_cpu (SysprofHostinfoSource *self)
{
gchar cpu[64] = { 0 };
glong user;
glong sys;
glong nice;
glong idle;
glong iowait;
glong irq;
glong softirq;
glong steal;
glong guest;
glong guest_nice;
glong user_calc;
glong system_calc;
glong nice_calc;
glong idle_calc;
glong iowait_calc;
glong irq_calc;
glong softirq_calc;
glong steal_calc;
glong guest_calc;
glong guest_nice_calc;
glong total;
gchar *line;
gint ret;
gint id;
if (read_stat (self))
{
line = self->stat_buf;
for (gsize i = 0; self->stat_buf[i]; i++)
{
if (self->stat_buf[i] == '\n')
{
self->stat_buf[i] = '\0';
if (strncmp (line, "cpu", 3) == 0)
{
if (isdigit (line[3]))
{
CpuInfo *cpu_info;
user = nice = sys = idle = id = 0;
ret = sscanf (line, "%s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
cpu, &user, &nice, &sys, &idle,
&iowait, &irq, &softirq, &steal, &guest, &guest_nice);
if (ret != 11)
goto next;
ret = sscanf(cpu, "cpu%d", &id);
if (ret != 1 || id < 0 || id >= self->n_cpu)
goto next;
cpu_info = &g_array_index (self->cpu_info, CpuInfo, id);
user_calc = user - cpu_info->last_user;
nice_calc = nice - cpu_info->last_nice;
system_calc = sys - cpu_info->last_system;
idle_calc = idle - cpu_info->last_idle;
iowait_calc = iowait - cpu_info->last_iowait;
irq_calc = irq - cpu_info->last_irq;
softirq_calc = softirq - cpu_info->last_softirq;
steal_calc = steal - cpu_info->last_steal;
guest_calc = guest - cpu_info->last_guest;
guest_nice_calc = guest_nice - cpu_info->last_guest_nice;
total = user_calc + nice_calc + system_calc + idle_calc + iowait_calc + irq_calc + softirq_calc + steal_calc + guest_calc + guest_nice_calc;
cpu_info->total = ((total - idle_calc) / (gdouble)total) * 100.0;
cpu_info->last_user = user;
cpu_info->last_nice = nice;
cpu_info->last_idle = idle;
cpu_info->last_system = sys;
cpu_info->last_iowait = iowait;
cpu_info->last_irq = irq;
cpu_info->last_softirq = softirq;
cpu_info->last_steal = steal;
cpu_info->last_guest = guest;
cpu_info->last_guest_nice = guest_nice;
}
}
else
{
/* CPU info comes first. Skip further lines. */
break;
}
next:
line = &self->stat_buf[i + 1];
}
}
}
}
static gdouble
get_cpu_freq (SysprofHostinfoSource *self,
guint cpu)
{
const CpuFreq *freq;
g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self));
g_assert (cpu < self->freqs->len);
freq = &g_array_index (self->freqs, CpuFreq, cpu);
if (freq->stat_fd > -1)
{
gchar buf[128];
gssize len;
lseek (freq->stat_fd, 0, SEEK_SET);
len = read (freq->stat_fd, buf, sizeof buf - 1);
if (len > 0 && len < sizeof buf)
{
gint64 val;
buf[len] = 0;
g_strstrip (buf);
val = g_ascii_strtoll (buf, NULL, 10);
return (gdouble)val / (gdouble)freq->max * 100.0;
}
}
return 0.0;
}
static void
publish_cpu (SysprofHostinfoSource *self)
{
SysprofCaptureCounterValue *counter_values;
guint *counter_ids;
glong total_usage = 0;
counter_ids = alloca (sizeof *counter_ids * (self->n_cpu * 2 + 1));
counter_values = alloca (sizeof *counter_values * (self->n_cpu * 2 + 1));
for (guint i = 0; i < self->n_cpu; i++)
{
CpuInfo *info = &g_array_index (self->cpu_info, CpuInfo, i);
SysprofCaptureCounterValue *value = &counter_values[i*2];
guint *id = &counter_ids[i*2];
*id = info->counter_base;
value->vdbl = info->total;
id++;
value++;
*id = info->counter_base + 1;
value->vdbl = get_cpu_freq (self, i);
total_usage += info->total;
}
/* Add combined counter */
counter_ids[self->n_cpu * 2] = self->combined_id;
counter_values[self->n_cpu * 2].vdbl = total_usage / (gdouble)self->n_cpu;
sysprof_capture_writer_set_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
counter_ids,
counter_values,
self->n_cpu * 2 + 1);
}
static gboolean
collect_hostinfo_cb (gpointer data)
{
SysprofHostinfoSource *self = data;
g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self));
poll_cpu (self);
publish_cpu (self);
return G_SOURCE_CONTINUE;
}
static void
sysprof_hostinfo_source_finalize (GObject *object)
{
SysprofHostinfoSource *self = (SysprofHostinfoSource *)object;
if (self->handler)
{
g_source_remove (self->handler);
self->handler = 0;
}
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
g_clear_pointer (&self->cpu_info, g_array_unref);
g_clear_pointer (&self->stat_buf, g_free);
g_clear_pointer (&self->freqs, g_array_unref);
G_OBJECT_CLASS (sysprof_hostinfo_source_parent_class)->finalize (object);
}
static void
sysprof_hostinfo_source_class_init (SysprofHostinfoSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_hostinfo_source_finalize;
}
static void
sysprof_hostinfo_source_init (SysprofHostinfoSource *self)
{
self->stat_fd = -1;
self->cpu_info = g_array_new (FALSE, TRUE, sizeof (CpuInfo));
self->stat_buf = g_malloc (PROC_STAT_BUF_SIZE);
self->freqs = g_array_new (FALSE, FALSE, sizeof (CpuFreq));
}
static void
sysprof_hostinfo_source_set_writer (SysprofSource *source,
SysprofCaptureWriter *writer)
{
SysprofHostinfoSource *self = (SysprofHostinfoSource *)source;
g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self));
g_assert (writer != NULL);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
self->writer = sysprof_capture_writer_ref (writer);
}
static void
sysprof_hostinfo_source_start (SysprofSource *source)
{
SysprofHostinfoSource *self = (SysprofHostinfoSource *)source;
g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self));
/* 20 samples per second */
self->handler = g_timeout_add (1000/20, collect_hostinfo_cb, self);
}
static void
sysprof_hostinfo_source_stop (SysprofSource *source)
{
SysprofHostinfoSource *self = (SysprofHostinfoSource *)source;
g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self));
g_source_remove (self->handler);
self->handler = 0;
if (self->stat_fd != -1)
{
close (self->stat_fd);
self->stat_fd = -1;
}
for (guint i = 0; i < self->freqs->len; i++)
{
CpuFreq *freq = &g_array_index (self->freqs, CpuFreq, i);
if (freq->stat_fd != -1)
close (freq->stat_fd);
}
if (self->freqs->len > 0)
g_array_remove_range (self->freqs, 0, self->freqs->len);
sysprof_source_emit_finished (SYSPROF_SOURCE (self));
}
static void
sysprof_hostinfo_source_prepare (SysprofSource *source)
{
SysprofHostinfoSource *self = (SysprofHostinfoSource *)source;
SysprofCaptureCounter *counters;
SysprofCaptureCounter *combined;
gint cpuinfo_fd;
g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self));
g_assert (self->writer != NULL);
/* We can generally get this even in containers */
if (-1 != (cpuinfo_fd = g_open ("/proc/cpuinfo", O_RDONLY)))
{
sysprof_capture_writer_add_file_fd (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
"/proc/cpuinfo",
cpuinfo_fd);
close (cpuinfo_fd);
}
self->stat_fd = open ("/proc/stat", O_RDONLY);
self->n_cpu = g_get_num_processors ();
g_array_set_size (self->cpu_info, 0);
counters = alloca (sizeof *counters * (self->n_cpu * 2 + 1));
for (guint i = 0; i < self->n_cpu; i++)
{
g_autofree gchar *max_path = NULL;
g_autofree gchar *cur_path = NULL;
g_autofree gchar *maxstr = NULL;
SysprofCaptureCounter *ctr = &counters[i*2];
CpuInfo info = { 0 };
CpuFreq freq = { 0 };
/*
* Request 2 counter values.
* One for CPU and one for Frequency.
*/
info.counter_base = sysprof_capture_writer_request_counter (self->writer, 2);
/*
* Define counters for capture file.
*/
ctr->id = info.counter_base;
ctr->type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
ctr->value.vdbl = 0;
g_strlcpy (ctr->category, "CPU Percent", sizeof ctr->category);
g_snprintf (ctr->name, sizeof ctr->name, "Total CPU %d", i);
g_snprintf (ctr->description, sizeof ctr->description,
"Total CPU usage %d", i);
ctr++;
max_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_max_freq", i);
cur_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i);
if (g_file_get_contents (max_path, &maxstr, NULL, NULL))
{
g_strstrip (maxstr);
freq.max = g_ascii_strtoll (maxstr, NULL, 10);
}
freq.stat_fd = -1;
sysprof_helpers_get_proc_fd (sysprof_helpers_get_default (),
cur_path, NULL, &freq.stat_fd, NULL);
g_array_append_val (self->freqs, freq);
ctr->id = info.counter_base + 1;
ctr->type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
ctr->value.vdbl = 0;
g_strlcpy (ctr->category, "CPU Frequency", sizeof ctr->category);
g_snprintf (ctr->name, sizeof ctr->name, "CPU %d", i);
g_snprintf (ctr->description, sizeof ctr->description,
"Frequency of CPU %d", i);
g_array_append_val (self->cpu_info, info);
}
/* Now add combined counter */
self->combined_id = sysprof_capture_writer_request_counter (self->writer, 1);
combined = &counters[self->n_cpu * 2];
combined->id = self->combined_id;
combined->type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
combined->value.vdbl = 0;
g_strlcpy (combined->category, "CPU Percent", sizeof combined->category);
g_snprintf (combined->name, sizeof combined->name, "Combined");
g_snprintf (combined->description, sizeof combined->description, "Combined CPU usage");
sysprof_capture_writer_define_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
counters,
self->n_cpu * 2 + 1);
sysprof_source_emit_ready (SYSPROF_SOURCE (self));
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->set_writer = sysprof_hostinfo_source_set_writer;
iface->prepare = sysprof_hostinfo_source_prepare;
iface->start = sysprof_hostinfo_source_start;
iface->stop = sysprof_hostinfo_source_stop;
}

View File

@ -1,39 +0,0 @@
/* sysprof-hostinfo-source.h
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_HOSTINFO_SOURCE (sysprof_hostinfo_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofHostinfoSource, sysprof_hostinfo_source, SYSPROF, HOSTINFO_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_hostinfo_source_new (void);
G_END_DECLS

View File

@ -1,125 +0,0 @@
/* sysprof-jitmap-symbol-resolver.c
*
* Copyright 2016-2019 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 "sysprof-kernel-symbol.h"
#include "sysprof-jitmap-symbol-resolver.h"
struct _SysprofJitmapSymbolResolver
{
GObject parent_instance;
GHashTable *jitmap;
};
static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface);
G_DEFINE_TYPE_EXTENDED (SysprofJitmapSymbolResolver,
sysprof_jitmap_symbol_resolver,
G_TYPE_OBJECT,
0,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER,
symbol_resolver_iface_init))
static void
sysprof_jitmap_symbol_resolver_finalize (GObject *object)
{
SysprofJitmapSymbolResolver *self = (SysprofJitmapSymbolResolver *)object;
g_clear_pointer (&self->jitmap, g_hash_table_unref);
G_OBJECT_CLASS (sysprof_jitmap_symbol_resolver_parent_class)->finalize (object);
}
static void
sysprof_jitmap_symbol_resolver_class_init (SysprofJitmapSymbolResolverClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_jitmap_symbol_resolver_finalize;
}
static void
sysprof_jitmap_symbol_resolver_init (SysprofJitmapSymbolResolver *self)
{
self->jitmap = g_hash_table_new_full (NULL, NULL, NULL, g_free);
}
static void
sysprof_jitmap_symbol_resolver_load (SysprofSymbolResolver *resolver,
SysprofCaptureReader *reader)
{
SysprofJitmapSymbolResolver *self = (SysprofJitmapSymbolResolver *)resolver;
SysprofCaptureFrameType type;
g_assert (SYSPROF_IS_JITMAP_SYMBOL_RESOLVER (self));
g_assert (reader != NULL);
while (sysprof_capture_reader_peek_type (reader, &type))
{
const SysprofCaptureJitmap *jitmap;
SysprofCaptureJitmapIter iter;
SysprofCaptureAddress addr;
const gchar *str;
if (type != SYSPROF_CAPTURE_FRAME_JITMAP)
{
if (!sysprof_capture_reader_skip (reader))
return;
continue;
}
if (!(jitmap = sysprof_capture_reader_read_jitmap (reader)))
return;
sysprof_capture_jitmap_iter_init (&iter, jitmap);
while (sysprof_capture_jitmap_iter_next (&iter, &addr, &str))
g_hash_table_insert (self->jitmap, GSIZE_TO_POINTER (addr), g_strdup (str));
}
}
static gchar *
sysprof_jitmap_symbol_resolver_resolve (SysprofSymbolResolver *resolver,
guint64 time,
GPid pid,
SysprofCaptureAddress address,
GQuark *tag)
{
SysprofJitmapSymbolResolver *self = (SysprofJitmapSymbolResolver *)resolver;
g_assert (SYSPROF_IS_JITMAP_SYMBOL_RESOLVER (self));
*tag = 0;
return g_strdup (g_hash_table_lookup (self->jitmap, GSIZE_TO_POINTER (address)));
}
static void
symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface)
{
iface->load = sysprof_jitmap_symbol_resolver_load;
iface->resolve = sysprof_jitmap_symbol_resolver_resolve;
}
SysprofSymbolResolver *
sysprof_jitmap_symbol_resolver_new (void)
{
return g_object_new (SYSPROF_TYPE_JITMAP_SYMBOL_RESOLVER, NULL);
}

View File

@ -1,38 +0,0 @@
/* sysprof-jitmap-symbol-resolver.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-symbol-resolver.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_JITMAP_SYMBOL_RESOLVER (sysprof_jitmap_symbol_resolver_get_type())
G_DECLARE_FINAL_TYPE (SysprofJitmapSymbolResolver, sysprof_jitmap_symbol_resolver, SYSPROF, JITMAP_SYMBOL_RESOLVER, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbolResolver *sysprof_jitmap_symbol_resolver_new (void);
G_END_DECLS

View File

@ -1,168 +0,0 @@
/* sysprof-kallsyms.c
*
* Copyright 2018-2019 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
*/
#define G_LOG_DOMAIN "sysprof-kallsyms"
#include "config.h"
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include "sysprof-kallsyms.h"
struct _SysprofKallsyms
{
gchar *buf;
gsize buflen;
gchar *endptr;
gchar *iter;
};
void
sysprof_kallsyms_free (SysprofKallsyms *self)
{
if (self != NULL)
{
g_clear_pointer (&self->buf, g_free);
g_slice_free (SysprofKallsyms, self);
}
}
SysprofKallsyms *
sysprof_kallsyms_new_take (gchar *data)
{
g_autoptr(SysprofKallsyms) self = NULL;
self = g_slice_new0 (SysprofKallsyms);
self->buf = g_steal_pointer (&data);
self->buflen = strlen (self->buf);
self->endptr = self->buf + self->buflen;
self->iter = self->buf;
return g_steal_pointer (&self);
}
SysprofKallsyms *
sysprof_kallsyms_new (const gchar *path)
{
g_autoptr(SysprofKallsyms) self = NULL;
if (path == NULL)
path = "/proc/kallsyms";
self = g_slice_new0 (SysprofKallsyms);
if (!g_file_get_contents (path, &self->buf, &self->buflen, NULL))
return NULL;
self->iter = self->buf;
self->endptr = self->buf + self->buflen;
return g_steal_pointer (&self);
}
gboolean
sysprof_kallsyms_next (SysprofKallsyms *self,
const gchar **name,
guint64 *address,
guint8 *type)
{
guint64 addr;
char *tok;
char *pptr;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (self->buf != NULL, FALSE);
g_return_val_if_fail (self->buflen > 0, FALSE);
g_return_val_if_fail (self->iter != NULL, FALSE);
g_return_val_if_fail (self->endptr != NULL, FALSE);
try_next:
if (self->iter >= self->endptr)
return FALSE;
tok = strtok_r (self->iter, " \t\n", &self->iter);
if (!tok || *tok == 0)
return FALSE;
if (*tok == '[')
{
tok = strtok_r (self->iter, " \t\n", &self->iter);
if (!tok || *tok == 0)
return FALSE;
}
/* We'll keep going if we fail to parse, (null) usually, so that we
* just skip to the next line.
*/
addr = g_ascii_strtoull (tok, &pptr, 16);
if ((pptr == tok) || (addr == G_MAXUINT64 && errno == ERANGE) || (addr == 0 && errno == EINVAL))
addr = 0;
*address = addr;
if (self->iter >= self->endptr)
return FALSE;
tok = strtok_r (self->iter, " \t\n", &self->iter);
if (!tok || *tok == 0)
return FALSE;
switch (*tok)
{
case 'A':
case 'B':
case 'D':
case 'R':
case 'T':
case 'V':
case 'W':
case 'a':
case 'b':
case 'd':
case 'r':
case 't':
case 'w':
*type = *tok;
break;
default:
return FALSE;
}
if (self->iter >= self->endptr)
return FALSE;
tok = strtok_r (self->iter, " \t\n", &self->iter);
if (!tok || *tok == 0)
return FALSE;
if (self->iter >= self->endptr)
return FALSE;
if (addr == 0)
goto try_next;
*name = tok;
return TRUE;
}

View File

@ -1,39 +0,0 @@
/* sysprof-kallsyms.h
*
* Copyright 2018-2019 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
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
typedef struct _SysprofKallsyms SysprofKallsyms;
SysprofKallsyms *sysprof_kallsyms_new (const gchar *path);
SysprofKallsyms *sysprof_kallsyms_new_take (gchar *data);
gboolean sysprof_kallsyms_next (SysprofKallsyms *self,
const gchar **name,
guint64 *address,
guint8 *type);
void sysprof_kallsyms_free (SysprofKallsyms *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofKallsyms, sysprof_kallsyms_free)
G_END_DECLS

View File

@ -1,156 +0,0 @@
/* sysprof-kernel-symbol-resolver.c
*
* Copyright 2016-2019 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
*/
#define G_LOG_DOMAIN "sysprof-kernel-symbol-resolver"
#include "config.h"
#include <unistd.h>
#include "sysprof-kallsyms.h"
#include "sysprof-kernel-symbol.h"
#include "sysprof-kernel-symbol-resolver.h"
#include "sysprof-private.h"
#include "sysprof-platform.h"
struct _SysprofKernelSymbolResolver
{
GObject parent_instance;
SysprofKernelSymbols *symbols;
};
static GQuark linux_quark;
static gchar *
sysprof_kernel_symbol_resolver_resolve_with_context (SysprofSymbolResolver *resolver,
guint64 time,
GPid pid,
SysprofAddressContext context,
SysprofCaptureAddress address,
GQuark *tag)
{
SysprofKernelSymbolResolver *self = (SysprofKernelSymbolResolver *)resolver;
const SysprofKernelSymbol *sym;
g_assert (SYSPROF_IS_SYMBOL_RESOLVER (self));
g_assert (tag != NULL);
if (context != SYSPROF_ADDRESS_CONTEXT_KERNEL)
return NULL;
if (self->symbols == NULL)
return NULL;
if ((sym = _sysprof_kernel_symbols_lookup (self->symbols, address)))
{
*tag = linux_quark;
return g_strdup (sym->name);
}
return NULL;
}
static void
sysprof_kernel_symbol_resolver_load (SysprofSymbolResolver *resolver,
SysprofCaptureReader *reader)
{
static const guint8 zero[] = {0};
SysprofKernelSymbolResolver *self = (SysprofKernelSymbolResolver *)resolver;
g_autoptr(GByteArray) bytes = NULL;
g_autoptr(SysprofKallsyms) kallsyms = NULL;
guint8 buf[4096];
gint data_fd;
g_assert (SYSPROF_IS_KERNEL_SYMBOL_RESOLVER (self));
g_assert (reader != NULL);
/* If there is an embedded __symbols__ file, then we won't do anything
* because we want to use the symbols from the peer.
*/
if (sysprof_capture_reader_find_file (reader, "__symbols__"))
return;
sysprof_capture_reader_reset (reader);
if (-1 == (data_fd = sysprof_memfd_create ("[sysprof-kallsyms]")) ||
!sysprof_capture_reader_read_file_fd (reader, "/proc/kallsyms", data_fd))
{
if (data_fd != -1)
close (data_fd);
self->symbols = _sysprof_kernel_symbols_get_shared ();
return;
}
bytes = g_byte_array_new ();
lseek (data_fd, 0, SEEK_SET);
for (;;)
{
gssize len = read (data_fd, buf, sizeof buf);
if (len <= 0)
break;
g_byte_array_append (bytes, buf, len);
}
g_byte_array_append (bytes, zero, 1);
if (bytes->len > 1)
{
kallsyms = sysprof_kallsyms_new_take ((gchar *)g_byte_array_free (g_steal_pointer (&bytes), FALSE));
self->symbols = _sysprof_kernel_symbols_new_from_kallsyms (kallsyms);
}
else
{
self->symbols = _sysprof_kernel_symbols_get_shared ();
}
}
static void
symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface)
{
iface->load = sysprof_kernel_symbol_resolver_load;
iface->resolve_with_context = sysprof_kernel_symbol_resolver_resolve_with_context;
}
G_DEFINE_TYPE_WITH_CODE (SysprofKernelSymbolResolver,
sysprof_kernel_symbol_resolver,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER,
symbol_resolver_iface_init))
static void
sysprof_kernel_symbol_resolver_class_init (SysprofKernelSymbolResolverClass *klass)
{
linux_quark = g_quark_from_static_string ("Kernel");
}
static void
sysprof_kernel_symbol_resolver_init (SysprofKernelSymbolResolver *skernel)
{
}
SysprofSymbolResolver *
sysprof_kernel_symbol_resolver_new (void)
{
return g_object_new (SYSPROF_TYPE_KERNEL_SYMBOL_RESOLVER, NULL);
}

View File

@ -1,38 +0,0 @@
/* sysprof-kernel-symbol-resolver.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-symbol-resolver.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_KERNEL_SYMBOL_RESOLVER (sysprof_kernel_symbol_resolver_get_type())
G_DECLARE_FINAL_TYPE (SysprofKernelSymbolResolver, sysprof_kernel_symbol_resolver, SYSPROF, KERNEL_SYMBOL_RESOLVER, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbolResolver *sysprof_kernel_symbol_resolver_new (void);
G_END_DECLS

View File

@ -1,252 +0,0 @@
/* sysprof-kernel-symbol.c
*
* Copyright 2016-2019 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
*/
#define G_LOG_DOMAIN "sysprof-kernel-symbol"
#include "config.h"
#include <gio/gio.h>
#include <sysprof-capture.h>
#include "sysprof-helpers.h"
#include "sysprof-kallsyms.h"
#include "sysprof-kernel-symbol.h"
#include "sysprof-private.h"
static G_LOCK_DEFINE (kernel_lock);
static GStringChunk *kernel_symbol_strs;
static GHashTable *kernel_symbols_skip_hash;
static const gchar *kernel_symbols_skip[] = {
/* IRQ stack */
"common_interrupt",
"apic_timer_interrupt",
"smp_apic_timer_interrupt",
"hrtimer_interrupt",
"__run_hrtimer",
"perf_swevent_hrtimer",
"perf_event_overflow",
"__perf_event_overflow",
"perf_prepare_sample",
"perf_callchain",
"perf_swcounter_hrtimer",
"perf_counter_overflow",
"__perf_counter_overflow",
"perf_counter_output",
/* NMI stack */
"nmi_stack_correct",
"do_nmi",
"notify_die",
"atomic_notifier_call_chain",
"notifier_call_chain",
"perf_event_nmi_handler",
"perf_counter_nmi_handler",
"intel_pmu_handle_irq",
"perf_event_overflow",
"perf_counter_overflow",
"__perf_event_overflow",
"perf_prepare_sample",
"perf_callchain",
};
static inline gboolean
type_is_ignored (guint8 type)
{
/* Only allow symbols in the text (code) section */
return (type != 't' && type != 'T');
}
static gint
sysprof_kernel_symbol_compare (gconstpointer a,
gconstpointer b)
{
const SysprofKernelSymbol *syma = a;
const SysprofKernelSymbol *symb = b;
if (syma->address > symb->address)
return 1;
else if (syma->address == symb->address)
return 0;
else
return -1;
}
static void
do_shared_init (void)
{
static gsize once;
if (g_once_init_enter (&once))
{
g_autoptr(GHashTable) skip = NULL;
kernel_symbol_strs = g_string_chunk_new (4096 * 4);
skip = g_hash_table_new (g_str_hash, g_str_equal);
for (guint i = 0; i < G_N_ELEMENTS (kernel_symbols_skip); i++)
g_hash_table_insert (skip, (gchar *)kernel_symbols_skip[i], NULL);
kernel_symbols_skip_hash = g_steal_pointer (&skip);
g_once_init_leave (&once, TRUE);
}
}
SysprofKernelSymbols *
_sysprof_kernel_symbols_new_from_kallsyms (SysprofKallsyms *kallsyms)
{
static const SysprofKernelSymbol empty = {0};
SysprofKernelSymbols *self;
const gchar *name;
guint64 addr;
guint8 type;
do_shared_init ();
g_return_val_if_fail (kallsyms != NULL, NULL);
self = g_array_new (FALSE, FALSE, sizeof (SysprofKernelSymbol));
G_LOCK (kernel_lock);
while (sysprof_kallsyms_next (kallsyms, &name, &addr, &type))
{
if (!type_is_ignored (type))
{
SysprofKernelSymbol sym;
sym.address = addr;
sym.name = g_string_chunk_insert_const (kernel_symbol_strs, name);
g_array_append_val (self, sym);
}
}
g_array_sort (self, sysprof_kernel_symbol_compare);
/* Always add a trailing node */
g_array_append_val (self, empty);
G_UNLOCK (kernel_lock);
return g_steal_pointer (&self);
}
SysprofKernelSymbols *
_sysprof_kernel_symbols_get_shared (void)
{
static SysprofKernelSymbols *shared;
static SysprofKernelSymbols empty[] = { 0 };
if (shared == NULL)
{
#ifdef __linux__
SysprofHelpers *helpers = sysprof_helpers_get_default ();
g_autofree gchar *contents = NULL;
if (sysprof_helpers_get_proc_file (helpers, "/proc/kallsyms", NULL, &contents, NULL))
{
g_autoptr(SysprofKallsyms) kallsyms = sysprof_kallsyms_new_take (g_steal_pointer (&contents));
shared = _sysprof_kernel_symbols_new_from_kallsyms (kallsyms);
}
#endif
if (shared == NULL)
shared = empty;
}
return shared;
}
static const SysprofKernelSymbol *
sysprof_kernel_symbol_lookup (SysprofKernelSymbol *symbols,
SysprofCaptureAddress address,
guint first,
guint last)
{
if (symbols == NULL)
return NULL;
if (address >= symbols [last].address)
{
return &symbols [last];
}
else if (last - first < 3)
{
while (last >= first)
{
if (address >= symbols[last].address)
return &symbols [last];
last--;
}
return NULL;
}
else
{
int mid = (first + last) / 2;
if (symbols [mid].address > address)
return sysprof_kernel_symbol_lookup (symbols, address, first, mid);
else
return sysprof_kernel_symbol_lookup (symbols, address, mid, last);
}
}
/*
* sysprof_kernel_symbols_lookup:
* @self: the symbol data to lookup
* @address: the address of the instruction pointer
*
* Locates the kernel symbol that contains @address.
*
* Returns: (transfer none): An #SysprofKernelSymbol or %NULL.
*/
const SysprofKernelSymbol *
_sysprof_kernel_symbols_lookup (const SysprofKernelSymbols *self,
SysprofCaptureAddress address)
{
const SysprofKernelSymbol *first;
const SysprofKernelSymbol *ret;
g_assert (self != NULL);
if (self->len < 2)
return NULL;
/* Short circuit if this is out of range */
first = &g_array_index (self, SysprofKernelSymbol, 0);
if (address < first->address)
return NULL;
ret = sysprof_kernel_symbol_lookup ((SysprofKernelSymbol *)(gpointer)self->data,
address,
0,
/* 1 for right-most, 1 for empty node */
self->len - 2);
/* We resolve all symbols, including ignored symbols so that we
* don't give back the wrong function juxtapose an ignored func.
*/
if (ret != NULL && g_hash_table_contains (kernel_symbols_skip_hash, ret->name))
return NULL;
return ret;
}

View File

@ -1,39 +0,0 @@
/* sysprof-kernel-symbol.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include <glib.h>
#include "sysprof-capture-types.h"
G_BEGIN_DECLS
typedef struct
{
SysprofCaptureAddress address;
const gchar *name;
} SysprofKernelSymbol;
G_END_DECLS

View File

@ -1,122 +0,0 @@
/* sysprof-line-reader.c
*
* Copyright 2015-2019 Christian Hergert <christian@hergert.me>
*
* 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 <string.h>
#include "sysprof-line-reader.h"
struct _SysprofLineReader
{
const gchar *contents;
gsize length;
gsize pos;
};
void
sysprof_line_reader_free (SysprofLineReader *self)
{
g_slice_free (SysprofLineReader, self);
}
/**
* sysprof_line_reader_new:
* @contents: The buffer to read lines from
* @length: the length of @buffer in bytes
*
* Creates a new #SysprofLineReader for the contents provided. @contents are not
* copied and therefore it is a programming error to free contents before
* freeing the #SysprofLineReader structure.
*
* Use sysprof_line_reader_next() to read through the lines of the buffer.
*
* Returns: (transfer full): A new #SysprofLineReader that should be freed with
* sysprof_line_reader_free() when no longer in use.
*/
SysprofLineReader *
sysprof_line_reader_new (const gchar *contents,
gssize length)
{
SysprofLineReader *self = g_slice_new (SysprofLineReader);
if (contents == NULL)
{
contents = "";
length = 0;
}
else if (length < 0)
{
length = strlen (contents);
}
self->contents = contents;
self->length = length;
self->pos = 0;
return self;
}
/**
* sysprof_line_reader_next:
* @self: the #SysprofLineReader
* @length: a location for the length of the line in bytes
*
* Moves forward to the beginning of the next line in the buffer. No changes to
* the buffer are made, and the result is a pointer within the string passed as
* @contents in sysprof_line_reader_init(). Since the line most likely will not be
* terminated with a NULL byte, you must provide @length to determine the
* length of the line.
*
* Using "line[length]" will place you on the \n that was found for the line.
* However, to perform this safely, you need to know that your string was
* either \0 terminated to begin with, or that your buffer provides enough space
* to guarantee you can dereference past the last "textual content" of the
* buffer.
*
* Returns: (nullable) (transfer none): The beginning of the line within the buffer
*/
const gchar *
sysprof_line_reader_next (SysprofLineReader *self,
gsize *length)
{
const gchar *ret;
const gchar *endptr;
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (length != NULL, NULL);
if ((self->contents == NULL) || (self->pos >= self->length))
{
*length = 0;
return NULL;
}
ret = &self->contents [self->pos];
endptr = memchr (ret, '\n', self->length - self->pos);
if (G_UNLIKELY (endptr == NULL))
endptr = &self->contents [self->length];
*length = (endptr - ret);
self->pos += *length + 1;
return ret;
}

View File

@ -1,40 +0,0 @@
/* sysprof-line-reader.h
*
* Copyright 2015-2019 Christian Hergert <christian@hergert.me>
*
* 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
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
typedef struct _SysprofLineReader SysprofLineReader;
G_GNUC_INTERNAL
SysprofLineReader *sysprof_line_reader_new (const gchar *contents,
gssize length);
G_GNUC_INTERNAL
void sysprof_line_reader_free (SysprofLineReader *self);
G_GNUC_INTERNAL
const gchar *sysprof_line_reader_next (SysprofLineReader *self,
gsize *length);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofLineReader, sysprof_line_reader_free)
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +0,0 @@
/* sysprof-local-profiler.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-profiler.h"
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_LOCAL_PROFILER (sysprof_local_profiler_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_DERIVABLE_TYPE (SysprofLocalProfiler, sysprof_local_profiler, SYSPROF, LOCAL_PROFILER, GObject)
struct _SysprofLocalProfilerClass
{
GObjectClass parent_class;
gpointer padding[8];
};
SYSPROF_AVAILABLE_IN_ALL
SysprofProfiler *sysprof_local_profiler_new (void);
SYSPROF_AVAILABLE_IN_ALL
SysprofProfiler *sysprof_local_profiler_new_replay (SysprofCaptureReader *reader);
SYSPROF_AVAILABLE_IN_3_46
void sysprof_local_profiler_set_inherit_stdin (SysprofLocalProfiler *self,
gboolean inherit_stdin);
G_END_DECLS

View File

@ -1,143 +0,0 @@
/* sysprof-map-lookaside.c
*
* Copyright 2016-2019 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 <glib.h>
#include "sysprof-map-lookaside.h"
static gint
sysprof_map_compare (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
const SysprofMap *map_a = a;
const SysprofMap *map_b = b;
return sysprof_capture_address_compare (map_a->start, map_b->start);
}
static gint
sysprof_map_compare_in_range (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
const SysprofMap *map_a = a;
const SysprofMap *map_b = b;
/*
* map_b is the needle for the search.
* Only map_b->start is set.
*/
if ((map_b->start >= map_a->start) && (map_b->start < map_a->end))
return 0;
return sysprof_capture_address_compare (map_a->start, map_b->start);
}
static void
sysprof_map_free (gpointer data)
{
SysprofMap *map = data;
g_slice_free (SysprofMap, map);
}
SysprofMapLookaside *
sysprof_map_lookaside_new (void)
{
SysprofMapLookaside *ret;
ret = g_slice_new0 (SysprofMapLookaside);
ret->seq = g_sequence_new (sysprof_map_free);
ret->chunk = g_string_chunk_new (4096);
ret->overlays = NULL;
return ret;
}
void
sysprof_map_lookaside_free (SysprofMapLookaside *self)
{
g_sequence_free (self->seq);
g_string_chunk_free (self->chunk);
g_slice_free (SysprofMapLookaside, self);
}
void
sysprof_map_lookaside_insert (SysprofMapLookaside *self,
const SysprofMap *map)
{
SysprofMap *copy;
g_assert (self != NULL);
g_assert (map != NULL);
copy = g_slice_new (SysprofMap);
copy->start = map->start;
copy->end = map->end;
copy->offset = map->offset;
copy->inode = map->inode;
copy->filename = g_string_chunk_insert_const (self->chunk, map->filename);
g_sequence_insert_sorted (self->seq, copy, sysprof_map_compare, NULL);
}
void
sysprof_map_lookaside_overlay (SysprofMapLookaside *self,
const gchar *src,
const gchar *dst)
{
SysprofMapOverlay overlay;
g_assert (self != NULL);
g_assert (src != NULL);
g_assert (dst != NULL);
if (!src[0] || !dst[0])
return;
if (self->overlays == NULL)
self->overlays = g_array_new (FALSE, FALSE, sizeof (SysprofMapOverlay));
overlay.src = g_string_chunk_insert_const (self->chunk, src);
overlay.dst = g_string_chunk_insert_const (self->chunk, dst);
g_array_append_val (self->overlays, overlay);
}
const SysprofMap *
sysprof_map_lookaside_lookup (SysprofMapLookaside *self,
SysprofCaptureAddress address)
{
SysprofMap map = { address };
GSequenceIter *iter;
g_assert (self != NULL);
iter = g_sequence_lookup (self->seq, &map, sysprof_map_compare_in_range, NULL);
if (iter != NULL)
return g_sequence_get (iter);
return NULL;
}

View File

@ -1,63 +0,0 @@
/* sysprof-map-lookaside.h
*
* Copyright 2016-2019 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
*/
#pragma once
#include <glib.h>
#include "sysprof-capture-types.h"
G_BEGIN_DECLS
typedef struct _SysprofMapOverlay
{
const char *src;
const char *dst;
} SysprofMapOverlay;
typedef struct _SysprofMapLookaside
{
GSequence *seq;
GStringChunk *chunk;
GArray *overlays;
} SysprofMapLookaside;
typedef struct
{
SysprofCaptureAddress start;
SysprofCaptureAddress end;
off_t offset;
ino_t inode;
const gchar *filename;
} SysprofMap;
SysprofMapLookaside *sysprof_map_lookaside_new (void);
void sysprof_map_lookaside_insert (SysprofMapLookaside *self,
const SysprofMap *map);
void sysprof_map_lookaside_overlay (SysprofMapLookaside *self,
const gchar *src,
const gchar *dst);
const SysprofMap *sysprof_map_lookaside_lookup (SysprofMapLookaside *self,
SysprofCaptureAddress address);
void sysprof_map_lookaside_free (SysprofMapLookaside *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMapLookaside, sysprof_map_lookaside_free)
G_END_DECLS

View File

@ -1,473 +0,0 @@
/* sysprof-memory-source.c
*
* Copyright 2018-2019 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
*/
#define G_LOG_DOMAIN "sysprof-memory-source"
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sysprof-capture.h>
#include <unistd.h>
#include "sysprof-helpers.h"
#include "sysprof-memory-source.h"
#define BUF_SIZE 4096
struct _SysprofMemorySource
{
GObject parent_instance;
/* Capture writer to deliver samples */
SysprofCaptureWriter *writer;
/* 4k stat buffer for reading proc */
gchar *stat_buf;
/* Array of MemStat */
GArray *mem_stats;
/* Timeout to perform polling */
guint timer_source;
};
typedef struct
{
GPid pid;
int stat_fd;
union {
struct {
SysprofCaptureCounterValue used;
gint64 total;
gint64 avail;
gint64 free;
} sys;
struct {
SysprofCaptureCounterValue used;
gint64 size;
gint64 resident;
gint64 shared;
gint64 text;
gint64 data;
} proc;
};
guint counter_ids[1];
} MemStat;
static void source_iface_init (SysprofSourceInterface *iface);
G_DEFINE_TYPE_WITH_CODE (SysprofMemorySource, sysprof_memory_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static GHashTable *keys;
static void
mem_stat_open (MemStat *st)
{
SysprofHelpers *helpers = sysprof_helpers_get_default ();
g_autoptr(GError) error = NULL;
g_assert (st != NULL);
g_assert (st->stat_fd == -1);
if (st->pid != -1)
{
g_autofree gchar *path = g_strdup_printf ("/proc/%d/statm", st->pid);
if (!sysprof_helpers_get_proc_fd (helpers, path, NULL, &st->stat_fd, &error))
g_warning ("Failed to access statm for pid %d: %s", st->pid, error->message);
}
else
{
if (!sysprof_helpers_get_proc_fd (helpers, "/proc/meminfo", NULL, &st->stat_fd, &error))
g_warning ("Failed to access /proc/statm: %s", error->message);
}
}
static void
mem_stat_close (MemStat *st)
{
g_assert (st != NULL);
if (st->stat_fd != -1)
{
close (st->stat_fd);
st->stat_fd = -1;
}
}
static void
mem_stat_parse_statm (MemStat *st,
gchar *buf)
{
g_assert (st != NULL);
g_assert (buf != NULL);
sscanf (buf,
"%"G_GINT64_FORMAT" "
"%"G_GINT64_FORMAT" "
"%"G_GINT64_FORMAT" "
"%"G_GINT64_FORMAT" "
"%*1c "
"%"G_GINT64_FORMAT,
&st->proc.size,
&st->proc.resident,
&st->proc.shared,
&st->proc.text,
&st->proc.data);
st->proc.used.vdbl = st->proc.size - st->proc.shared - st->proc.text - st->proc.data;
}
static void
mem_stat_parse_meminfo (MemStat *st,
gchar *buf)
{
gchar *bufptr = buf;
gchar *save = NULL;
g_assert (st != NULL);
g_assert (buf != NULL);
for (;;)
{
goffset off;
gchar *key;
gchar *value;
gchar *unit;
gint64 v64;
gint64 *v64ptr;
/* Get the data key name */
if (!(key = strtok_r (bufptr, " \n\t:", &save)))
break;
bufptr = NULL;
/* Offset from self to save value. Stop after getting to
* last value we care about.
*/
if (!(off = GPOINTER_TO_UINT (g_hash_table_lookup (keys, key))))
break;
/* Get the data value */
if (!(value = strtok_r (bufptr, " \n\t:", &save)))
break;
/* Parse the numeric value of this column */
v64 = g_ascii_strtoll (value, NULL, 10);
if ((v64 == G_MININT64 || v64 == G_MAXINT64) && errno == ERANGE)
break;
/* Get the data unit */
unit = strtok_r (bufptr, " \n\t:", &save);
if (g_strcmp0 (unit, "kB") == 0)
v64 *= 1024;
else if (g_strcmp0 (unit, "mB") == 0)
v64 *= 1024 * 1024;
v64ptr = (gint64 *)(gpointer)(((gchar *)st) + off);
*v64ptr = v64;
}
/* Create pre-compiled value for used to simplify display */
st->sys.used.vdbl = (gdouble)st->sys.total - (gdouble)st->sys.avail;
}
static void
mem_stat_poll (MemStat *st,
gchar *stat_buf)
{
gssize r;
g_assert (st != NULL);
g_assert (st->stat_fd != -1);
/* Seek to begin of FD to reset the proc read */
if ((r = lseek (st->stat_fd, 0, SEEK_SET)) < 0)
return;
/* Now read the updated proc buffer */
if ((r = read (st->stat_fd, stat_buf, BUF_SIZE)) < 0)
return;
if (r < BUF_SIZE)
stat_buf[r] = '\0';
stat_buf[BUF_SIZE-1] = '\0';
if (st->pid == -1)
mem_stat_parse_meminfo (st, stat_buf);
else
mem_stat_parse_statm (st, stat_buf);
}
static void
mem_stat_publish (MemStat *st,
SysprofCaptureWriter *writer,
gint64 current_time)
{
g_assert (st != NULL);
g_assert (writer != NULL);
sysprof_capture_writer_set_counters (writer,
current_time,
-1,
st->pid,
st->counter_ids,
st->pid == -1 ? &st->sys.used : &st->proc.used,
1);
}
/**
* sysprof_memory_source_new:
*
* Create a new #SysprofMemorySource.
*
* Use this source to record basic memory statistics about the system
* such as Available, Free, and Total Memory.
*
* Returns: (transfer full): a newly created #SysprofMemorySource
* Since: 3.32
*/
SysprofSource *
sysprof_memory_source_new (void)
{
return g_object_new (SYSPROF_TYPE_MEMORY_SOURCE, NULL);
}
static void
sysprof_memory_source_finalize (GObject *object)
{
SysprofMemorySource *self = (SysprofMemorySource *)object;
if (self->timer_source != 0)
{
g_source_remove (self->timer_source);
self->timer_source = 0;
}
g_clear_pointer (&self->stat_buf, g_free);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
g_clear_pointer (&self->mem_stats, g_array_unref);
G_OBJECT_CLASS (sysprof_memory_source_parent_class)->finalize (object);
}
static void
sysprof_memory_source_class_init (SysprofMemorySourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_memory_source_finalize;
keys = g_hash_table_new (g_str_hash, g_str_equal);
#define ADD_OFFSET(n,o) \
g_hash_table_insert (keys, (gchar *)n, GUINT_TO_POINTER (o))
ADD_OFFSET ("MemTotal", G_STRUCT_OFFSET (MemStat, sys.total));
ADD_OFFSET ("MemFree", G_STRUCT_OFFSET (MemStat, sys.free));
ADD_OFFSET ("MemAvailable", G_STRUCT_OFFSET (MemStat, sys.avail));
#undef ADD_OFFSET
}
static void
sysprof_memory_source_init (SysprofMemorySource *self)
{
self->stat_buf = g_malloc (BUF_SIZE);
self->mem_stats = g_array_new (FALSE, FALSE, sizeof (MemStat));
}
static void
sysprof_memory_source_set_writer (SysprofSource *source,
SysprofCaptureWriter *writer)
{
SysprofMemorySource *self = (SysprofMemorySource *)source;
g_assert (SYSPROF_IS_SOURCE (self));
g_assert (writer != NULL);
g_assert (self->writer == NULL);
self->writer = sysprof_capture_writer_ref (writer);
}
static void
sysprof_memory_source_prepare (SysprofSource *source)
{
SysprofMemorySource *self = (SysprofMemorySource *)source;
g_assert (SYSPROF_IS_MEMORY_SOURCE (self));
g_assert (self->writer != NULL);
/* If no pids are registered, setup a system memory stat */
if (self->mem_stats->len == 0)
{
MemStat st = {0};
st.pid = -1;
st.stat_fd = -1;
g_array_append_val (self->mem_stats, st);
}
/* Open FDs to all the necessary files in proc. We will re-use
* them to avoid re-opening files later.
*/
for (guint i = 0; i < self->mem_stats->len; i++)
{
MemStat *st = &g_array_index (self->mem_stats, MemStat, i);
SysprofCaptureCounter counters[5];
guint base;
mem_stat_open (st);
if (st->pid == -1)
{
base = sysprof_capture_writer_request_counter (self->writer, 1);
g_strlcpy (counters[0].category, "Memory", sizeof counters[0].category);
g_strlcpy (counters[0].name, "Used", sizeof counters[0].name);
g_strlcpy (counters[0].description, "Memory used by system", sizeof counters[0].description);
counters[0].id = st->counter_ids[0] = base;
counters[0].type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
counters[0].value.vdbl = 0;
sysprof_capture_writer_define_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
counters,
1);
}
else
{
base = sysprof_capture_writer_request_counter (self->writer, 1);
g_strlcpy (counters[0].category, "Memory", sizeof counters[0].category);
g_strlcpy (counters[0].name, "Used", sizeof counters[0].name);
g_strlcpy (counters[0].description, "Memory used by process", sizeof counters[0].description);
counters[0].id = st->counter_ids[0] = base;
counters[0].type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
counters[0].value.vdbl = 0;
sysprof_capture_writer_define_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
st->pid,
counters,
1);
}
}
sysprof_source_emit_ready (SYSPROF_SOURCE (self));
}
static gboolean
sysprof_memory_source_timer_cb (SysprofMemorySource *self)
{
gint64 current_time;
g_assert (SYSPROF_IS_MEMORY_SOURCE (self));
g_assert (self->writer != NULL);
current_time = sysprof_clock_get_current_time ();
for (guint i = 0; i < self->mem_stats->len; i++)
{
MemStat *st = &g_array_index (self->mem_stats, MemStat, i);
mem_stat_poll (st, self->stat_buf);
mem_stat_publish (st, self->writer, current_time);
}
return G_SOURCE_CONTINUE;
}
static void
sysprof_memory_source_start (SysprofSource *source)
{
SysprofMemorySource *self = (SysprofMemorySource *)source;
g_assert (SYSPROF_IS_MEMORY_SOURCE (self));
/* Poll 4x/sec for memory stats */
self->timer_source = g_timeout_add_full (G_PRIORITY_HIGH,
1000 / 4,
(GSourceFunc)sysprof_memory_source_timer_cb,
self,
NULL);
}
static void
sysprof_memory_source_stop (SysprofSource *source)
{
SysprofMemorySource *self = (SysprofMemorySource *)source;
g_assert (SYSPROF_IS_MEMORY_SOURCE (self));
if (self->timer_source != 0)
{
g_source_remove (self->timer_source);
self->timer_source = 0;
}
for (guint i = 0; i < self->mem_stats->len; i++)
{
MemStat *st = &g_array_index (self->mem_stats, MemStat, i);
mem_stat_close (st);
}
sysprof_source_emit_finished (source);
}
static void
sysprof_memory_source_add_pid (SysprofSource *source,
GPid pid)
{
SysprofMemorySource *self = (SysprofMemorySource *)source;
MemStat st = {0};
g_assert (SYSPROF_IS_MEMORY_SOURCE (self));
st.pid = pid;
st.stat_fd = -1;
g_array_append_val (self->mem_stats, st);
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->set_writer = sysprof_memory_source_set_writer;
iface->prepare = sysprof_memory_source_prepare;
iface->start = sysprof_memory_source_start;
iface->stop = sysprof_memory_source_stop;
iface->add_pid = sysprof_memory_source_add_pid;
}

View File

@ -1,40 +0,0 @@
/* sysprof-memory-source.h
*
* Copyright 2018-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-source.h"
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_MEMORY_SOURCE (sysprof_memory_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofMemorySource, sysprof_memory_source, SYSPROF, MEMORY_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_memory_source_new (void);
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@ -1,98 +0,0 @@
/* sysprof-memprof-profile.h
*
* 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-version-macros.h"
#include "sysprof-profile.h"
#include "sysprof-selection.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_MEMPROF_PROFILE (sysprof_memprof_profile_get_type())
/**
* SysprofMemprofMode:
* @SYSPROF_MEMPROF_MODE_SUMMARY: The summary profile
* @SYSPROF_MEMPROF_MODE_ALL_ALLOCS: find all allocations
* @SYSPROF_MEMPROF_MODE_TEMP_ALLOCS: find temporary allocations
* @SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS: find allocation leaks, Since 3.44
*
* The memprof profile mode.
*
* Since 3.44 @SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS is available
* to find leaked allocations.
*/
typedef enum
{
SYSPROF_MEMPROF_MODE_SUMMARY = 0,
SYSPROF_MEMPROF_MODE_ALL_ALLOCS = 1,
SYSPROF_MEMPROF_MODE_TEMP_ALLOCS = 2,
SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS = 3,
} SysprofMemprofMode;
typedef struct
{
gint64 n_allocs;
gint64 leaked_allocs;
gint64 leaked_allocs_size;
gint64 temp_allocs;
gint64 temp_allocs_size;
struct {
gint64 bucket;
gint64 n_allocs;
gint64 temp_allocs;
gint64 allocated;
} by_size[14];
/*< private >*/
gint64 _reserved[32];
} SysprofMemprofStats;
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofMemprofProfile, sysprof_memprof_profile, SYSPROF, MEMPROF_PROFILE, GObject)
SYSPROF_AVAILABLE_IN_3_36
SysprofProfile *sysprof_memprof_profile_new (void);
SYSPROF_AVAILABLE_IN_3_36
SysprofProfile *sysprof_memprof_profile_new_with_selection (SysprofSelection *selection);
SYSPROF_AVAILABLE_IN_3_36
void sysprof_memprof_profile_get_stats (SysprofMemprofProfile *self,
SysprofMemprofStats *stats);
SYSPROF_AVAILABLE_IN_3_36
SysprofMemprofMode sysprof_memprof_profile_get_mode (SysprofMemprofProfile *self);
SYSPROF_AVAILABLE_IN_3_36
void sysprof_memprof_profile_set_mode (SysprofMemprofProfile *self,
SysprofMemprofMode mode);
SYSPROF_AVAILABLE_IN_3_36
gpointer sysprof_memprof_profile_get_native (SysprofMemprofProfile *self);
SYSPROF_AVAILABLE_IN_3_36
gpointer sysprof_memprof_profile_get_stash (SysprofMemprofProfile *self);
SYSPROF_AVAILABLE_IN_3_36
gboolean sysprof_memprof_profile_is_empty (SysprofMemprofProfile *self);
SYSPROF_AVAILABLE_IN_3_36
GQuark sysprof_memprof_profile_get_tag (SysprofMemprofProfile *self,
const gchar *symbol);
G_END_DECLS

View File

@ -1,87 +0,0 @@
/* sysprof-memprof-source.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
*/
#define G_LOG_DOMAIN "sysprof-memprof-source"
#include "config.h"
#include "sysprof-memprof-source.h"
struct _SysprofMemprofSource
{
GObject parent_instance;
};
static void
sysprof_memprof_source_modify_spawn (SysprofSource *source,
SysprofSpawnable *spawnable)
{
g_assert (SYSPROF_IS_SOURCE (source));
g_assert (SYSPROF_IS_SPAWNABLE (spawnable));
sysprof_spawnable_setenv (spawnable, "G_SLICE", "always-malloc");
#ifdef __linux__
{
static const gchar so_path[] = PACKAGE_LIBDIR"/libsysprof-memory-"API_VERSION_S".so";
g_autofree gchar *freeme = NULL;
const gchar *ld_preload;
if (!(ld_preload = sysprof_spawnable_getenv (spawnable, "LD_PRELOAD")))
sysprof_spawnable_setenv (spawnable, "LD_PRELOAD", so_path);
else
sysprof_spawnable_setenv (spawnable,
"LD_PRELOAD",
(freeme = g_strdup_printf ("%s:%s", so_path, ld_preload)));
}
#endif
}
static void
sysprof_memprof_source_stop (SysprofSource *source)
{
sysprof_source_emit_finished (source);
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->modify_spawn = sysprof_memprof_source_modify_spawn;
iface->stop = sysprof_memprof_source_stop;
}
G_DEFINE_TYPE_WITH_CODE (SysprofMemprofSource, sysprof_memprof_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static void
sysprof_memprof_source_class_init (SysprofMemprofSourceClass *klass)
{
}
static void
sysprof_memprof_source_init (SysprofMemprofSource *self)
{
}
SysprofSource *
sysprof_memprof_source_new (void)
{
return g_object_new (SYSPROF_TYPE_MEMPROF_SOURCE, NULL);
}

View File

@ -1,35 +0,0 @@
/* sysprof-memprof-source.h
*
* 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
*/
#pragma once
#include "sysprof-tracefd-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_MEMPROF_SOURCE (sysprof_memprof_source_get_type())
SYSPROF_AVAILABLE_IN_3_36
G_DECLARE_FINAL_TYPE (SysprofMemprofSource, sysprof_memprof_source, SYSPROF, MEMPROF_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_3_36
SysprofSource *sysprof_memprof_source_new (void);
G_END_DECLS

View File

@ -1,352 +0,0 @@
/* sysprof-mountinfo.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-mountinfo"
#include "config.h"
#include "sysprof-mountinfo.h"
typedef struct
{
gchar *device;
gchar *mountpoint;
gchar *subvol;
} Mount;
typedef struct
{
gchar *host_path;
gchar *mount_path;
} Mountinfo;
struct _SysprofMountinfo
{
GArray *mounts;
GArray *mountinfos;
GHashTable *dircache;
};
enum {
COLUMN_MOUNT_ID = 0,
COLUMN_MOUNT_PARENT_ID,
COLUMN_MAJOR_MINOR,
COLUMN_ROOT,
COLUMN_MOUNT_POINT,
COLUMN_MOUNT_OPTIONS,
};
static void
mount_clear (gpointer data)
{
Mount *m = data;
g_free (m->device);
g_free (m->mountpoint);
g_free (m->subvol);
}
static void
mountinfo_clear (gpointer data)
{
Mountinfo *m = data;
g_free (m->host_path);
g_free (m->mount_path);
}
SysprofMountinfo *
sysprof_mountinfo_new (void)
{
SysprofMountinfo *self;
self = g_slice_new0 (SysprofMountinfo);
self->mounts = g_array_new (FALSE, FALSE, sizeof (Mount));
g_array_set_clear_func (self->mounts, mount_clear);
self->mountinfos = g_array_new (FALSE, FALSE, sizeof (Mountinfo));
g_array_set_clear_func (self->mountinfos, mountinfo_clear);
self->dircache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
return g_steal_pointer (&self);
}
void
sysprof_mountinfo_free (SysprofMountinfo *self)
{
g_clear_pointer (&self->mounts, g_array_unref);
g_clear_pointer (&self->mountinfos, g_array_unref);
g_clear_pointer (&self->dircache, g_hash_table_unref);
g_slice_free (SysprofMountinfo, self);
}
gchar *
sysprof_mountinfo_translate (SysprofMountinfo *self,
const gchar *path)
{
g_autofree gchar *dir = NULL;
const gchar *translate;
g_assert (self != NULL);
if (path == NULL)
return NULL;
/* First try the dircache by looking up the translated parent
* directory and appending basename to it.
*/
dir = g_path_get_dirname (path);
if ((translate = g_hash_table_lookup (self->dircache, dir)))
{
g_autofree gchar *name = g_path_get_basename (path);
return g_build_filename (translate, name, NULL);
}
for (guint i = 0; i < self->mountinfos->len; i++)
{
const Mountinfo *m = &g_array_index (self->mountinfos, Mountinfo, i);
if (g_str_has_prefix (path, m->mount_path))
{
gchar *ret;
ret = g_build_filename (m->host_path, path + strlen (m->mount_path), NULL);
g_hash_table_insert (self->dircache,
g_steal_pointer (&dir),
g_path_get_dirname (ret));
return ret;
}
}
return NULL;
}
static void
decode_space (gchar **str)
{
/* Replace encoded space "\040" with ' ' */
if (strstr (*str, "\\040"))
{
g_auto(GStrv) parts = g_strsplit (*str, "\\040", 0);
g_free (*str);
*str = g_strjoinv (" ", parts);
}
}
void
sysprof_mountinfo_parse_mounts (SysprofMountinfo *self,
const gchar *contents)
{
g_auto(GStrv) lines = NULL;
g_assert (self != NULL);
g_assert (self->mounts != NULL);
g_assert (contents != NULL);
lines = g_strsplit (contents, "\n", 0);
for (guint i = 0; lines[i]; i++)
{
g_auto(GStrv) parts = g_strsplit (lines[i], " ", 5);
g_autofree char *subvol = NULL;
const char *filesystem;
const char *mountpoint;
const char *device;
const char *options;
Mount m;
/* Field 0: device
* Field 1: mountpoint
* Field 2: filesystem
* Field 3: Options
* .. Ignored ..
*/
if (g_strv_length (parts) != 5)
continue;
for (guint j = 0; parts[j]; j++)
decode_space (&parts[j]);
device = parts[0];
mountpoint = parts[1];
filesystem = parts[2];
options = parts[3];
if (g_strcmp0 (filesystem, "btrfs") == 0)
{
g_auto(GStrv) opts = g_strsplit (options, ",", 0);
for (guint k = 0; opts[k]; k++)
{
if (g_str_has_prefix (opts[k], "subvol="))
{
subvol = g_strdup (opts[k] + strlen ("subvol="));
break;
}
}
}
m.device = g_strdup (device);
m.mountpoint = g_strdup (mountpoint);
m.subvol = g_steal_pointer (&subvol);
g_array_append_val (self->mounts, m);
}
}
void
sysprof_mountinfo_reset (SysprofMountinfo *self)
{
g_assert (self != NULL);
g_assert (self->mountinfos != NULL);
/* Keep mounts, but release mountinfos */
if (self->mountinfos->len)
g_array_remove_range (self->mountinfos, 0, self->mountinfos->len);
g_hash_table_remove_all (self->dircache);
}
static const gchar *
get_device_mount (SysprofMountinfo *self,
const gchar *device)
{
g_assert (self != NULL);
g_assert (self->mounts != NULL);
g_assert (device != NULL);
for (guint i = 0; i < self->mounts->len; i++)
{
const Mount *m = &g_array_index (self->mounts, Mount, i);
if (strcmp (device, m->device) == 0)
return m->mountpoint;
}
return NULL;
}
static void
sysprof_mountinfo_parse_mountinfo_line (SysprofMountinfo *self,
const gchar *line)
{
g_auto(GStrv) parts = NULL;
const gchar *prefix;
const gchar *src;
Mountinfo m;
gsize n_parts;
guint i;
g_assert (self != NULL);
g_assert (self->mounts != NULL);
g_assert (self->mountinfos != NULL);
parts = g_strsplit (line, " ", 0);
n_parts = g_strv_length (parts);
if (n_parts < 10)
return;
/* The device identifier is the 2nd column after "-" */
for (i = 5; i < n_parts; i++)
{
if (strcmp (parts[i], "-") == 0)
break;
}
if (i >= n_parts || parts[i][0] != '-' || parts[i+1] == NULL || parts[i+2] == NULL)
return;
prefix = get_device_mount (self, parts[i+2]);
src = parts[COLUMN_ROOT];
/* If this references a subvolume, try to find the mount by matching
* the subvolumne using the "src". This isn't exactly correct, but it's
* good enough to get btrfs stuff working for common installs.
*/
if (g_strcmp0 (parts[8], "btrfs") == 0)
{
const char *subvol = src;
for (i = 0; i < self->mounts->len; i++)
{
const Mount *mnt = &g_array_index (self->mounts, Mount, i);
if (g_strcmp0 (mnt->subvol, subvol) == 0)
{
src = mnt->mountpoint;
break;
}
}
}
while (*src == '/')
src++;
if (*src == 0)
return;
if (prefix != NULL)
m.host_path = g_build_filename (prefix, src, NULL);
else
m.host_path = g_strdup (src);
m.mount_path = g_strdup (parts[COLUMN_MOUNT_POINT]);
g_array_append_val (self->mountinfos, m);
}
static gint
sort_by_length (gconstpointer a,
gconstpointer b)
{
const Mountinfo *mpa = a;
const Mountinfo *mpb = b;
gsize alen = strlen (mpa->mount_path);
gsize blen = strlen (mpb->mount_path);
if (alen > blen)
return -1;
else if (blen > alen)
return 1;
else
return 0;
}
void
sysprof_mountinfo_parse_mountinfo (SysprofMountinfo *self,
const gchar *contents)
{
g_auto(GStrv) lines = NULL;
g_assert (self != NULL);
g_assert (self->mounts != NULL);
g_assert (self->mountinfos != NULL);
lines = g_strsplit (contents, "\n", 0);
for (guint i = 0; lines[i]; i++)
sysprof_mountinfo_parse_mountinfo_line (self, lines[i]);
g_array_sort (self->mountinfos, sort_by_length);
for (guint i = 0; i < self->mountinfos->len; i++)
{
const Mountinfo *m = &g_array_index (self->mountinfos, Mountinfo, i);
g_print ("MM %s => %s\n", m->host_path, m->mount_path);
}
}

View File

@ -1,41 +0,0 @@
/* sysprof-mountinfo.h
*
* Copyright 2019 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
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
typedef struct _SysprofMountinfo SysprofMountinfo;
SysprofMountinfo *sysprof_mountinfo_new (void);
void sysprof_mountinfo_parse_mounts (SysprofMountinfo *self,
const gchar *contents);
void sysprof_mountinfo_parse_mountinfo (SysprofMountinfo *self,
const gchar *contents);
void sysprof_mountinfo_reset (SysprofMountinfo *self);
gchar *sysprof_mountinfo_translate (SysprofMountinfo *self,
const gchar *path);
void sysprof_mountinfo_free (SysprofMountinfo *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMountinfo, sysprof_mountinfo_free)
G_END_DECLS

View File

@ -1,420 +0,0 @@
/* sysprof-netdev-source.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-netdev-source"
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <glib/gstdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sysprof-backport-autocleanups.h"
#include "sysprof-line-reader.h"
#include "sysprof-netdev-source.h"
#include "sysprof-helpers.h"
struct _SysprofNetdevSource
{
GObject parent_instance;
SysprofCaptureWriter *writer;
GArray *netdevs;
/* Combined (all devices) rx/tx counters */
guint combined_rx_id;
guint combined_tx_id;
/* FD for /proc/net/dev contents */
gint netdev_fd;
/* GSource ID for polling */
guint poll_source;
};
typedef struct
{
/* Counter IDs */
guint rx_bytes_id;
guint tx_bytes_id;
gchar iface[32];
gint64 rx_bytes;
gint64 rx_packets;
gint64 rx_errors;
gint64 rx_dropped;
gint64 rx_fifo;
gint64 rx_frame;
gint64 rx_compressed;
gint64 rx_multicast;
gint64 tx_bytes;
gint64 tx_packets;
gint64 tx_errors;
gint64 tx_dropped;
gint64 tx_fifo;
gint64 tx_collisions;
gint64 tx_carrier;
gint64 tx_compressed;
} Netdev;
static void source_iface_init (SysprofSourceInterface *);
G_DEFINE_TYPE_WITH_CODE (SysprofNetdevSource, sysprof_netdev_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static Netdev *
find_device_by_name (SysprofNetdevSource *self,
const gchar *name)
{
g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
g_assert (self->writer != NULL);
g_assert (name != NULL);
for (guint i = 0; i < self->netdevs->len; i++)
{
Netdev *netdev = &g_array_index (self->netdevs, Netdev, i);
if (strcmp (name, netdev->iface) == 0)
return netdev;
}
return NULL;
}
static Netdev *
register_counters_by_name (SysprofNetdevSource *self,
const gchar *name)
{
SysprofCaptureCounter ctr[2] = {{{0}}};
g_autofree gchar *rx = NULL;
g_autofree gchar *tx = NULL;
Netdev nd = {0};
g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
g_assert (name != NULL);
g_assert (self->writer != NULL);
rx = g_strdup_printf ("RX Bytes (%s)", name);
tx = g_strdup_printf ("TX Bytes (%s)", name);
nd.rx_bytes_id = sysprof_capture_writer_request_counter (self->writer, 1);
nd.tx_bytes_id = sysprof_capture_writer_request_counter (self->writer, 1);
g_strlcpy (nd.iface, name, sizeof nd.iface);
g_array_append_val (self->netdevs, nd);
g_strlcpy (ctr[0].category, "Network", sizeof ctr[0].category);
g_strlcpy (ctr[0].name, rx, sizeof ctr[0].name);
g_strlcpy (ctr[0].description, name, sizeof ctr[0].description);
ctr[0].id = nd.rx_bytes_id;
ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64;
ctr[0].value.v64 = 0;
g_strlcpy (ctr[1].category, "Network", sizeof ctr[1].category);
g_strlcpy (ctr[1].name, tx, sizeof ctr[1].name);
g_strlcpy (ctr[1].description, name, sizeof ctr[1].description);
ctr[1].id = nd.tx_bytes_id;
ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64;
ctr[1].value.v64 = 0;
sysprof_capture_writer_define_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
ctr, G_N_ELEMENTS (ctr));
return &g_array_index (self->netdevs, Netdev, self->netdevs->len - 1);
}
static gboolean
sysprof_netdev_source_get_is_ready (SysprofSource *source)
{
return TRUE;
}
static void
sysprof_netdev_source_prepare (SysprofSource *source)
{
SysprofNetdevSource *self = (SysprofNetdevSource *)source;
g_autoptr(GError) error = NULL;
SysprofCaptureCounter ctr[2] = {{{0}}};
g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
self->netdev_fd = g_open ("/proc/net/dev", O_RDONLY, 0);
if (self->netdev_fd == -1)
{
int errsv = errno;
error = g_error_new (G_FILE_ERROR,
g_file_error_from_errno (errsv),
"%s",
g_strerror (errsv));
sysprof_source_emit_failed (source, error);
return;
}
self->combined_rx_id = sysprof_capture_writer_request_counter (self->writer, 1);
self->combined_tx_id = sysprof_capture_writer_request_counter (self->writer, 1);
g_strlcpy (ctr[0].category, "Network", sizeof ctr[0].category);
g_strlcpy (ctr[0].name, "RX Bytes", sizeof ctr[0].name);
g_strlcpy (ctr[0].description, "Combined", sizeof ctr[0].description);
ctr[0].id = self->combined_rx_id;
ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64;
ctr[0].value.v64 = 0;
g_strlcpy (ctr[1].category, "Network", sizeof ctr[1].category);
g_strlcpy (ctr[1].name, "TX Bytes", sizeof ctr[1].name);
g_strlcpy (ctr[1].description, "Combined", sizeof ctr[1].description);
ctr[1].id = self->combined_tx_id;
ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64;
ctr[1].value.v64 = 0;
sysprof_capture_writer_define_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
ctr, G_N_ELEMENTS (ctr));
sysprof_source_emit_ready (source);
}
static gboolean
sysprof_netdev_source_poll_cb (gpointer data)
{
g_autoptr(SysprofLineReader) reader = NULL;
SysprofNetdevSource *self = data;
g_autoptr(GArray) counters = NULL;
g_autoptr(GArray) values = NULL;
gchar buf[4096*4];
gint64 combined_rx = 0;
gint64 combined_tx = 0;
gssize len;
gsize line_len;
gchar *line;
g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
if (self->netdev_fd == -1)
{
self->poll_source = 0;
return G_SOURCE_REMOVE;
}
/* Seek to 0 forces reload of data */
lseek (self->netdev_fd, 0, SEEK_SET);
len = read (self->netdev_fd, buf, sizeof buf - 1);
/* Bail for now unless we read enough data */
if (len > 0)
buf[len] = 0;
else
return G_SOURCE_CONTINUE;
counters = g_array_new (FALSE, FALSE, sizeof (guint));
values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue));
reader = sysprof_line_reader_new (buf, len);
#if 0
Entries looks like this...
------------------------------------------------------------------------------------------------------------------------------
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 10410 104 0 0 0 0 0 0 10410 104 0 0 0 0 0 0
eth0:1069675772 4197670 0 0 0 0 0 0 3221945712 3290571 0 0 0 0 0 0
------------------------------------------------------------------------------------------------------------------------------
#endif
/* Skip first two lines */
for (guint i = 0; i < 2; i++)
{
if (!(line = (gchar *)sysprof_line_reader_next (reader, &line_len)))
return G_SOURCE_CONTINUE;
}
while ((line = (gchar *)sysprof_line_reader_next (reader, &line_len)))
{
Netdev *nd;
gchar *name;
gchar *ptr = line;
line[line_len] = 0;
for (; *ptr && g_ascii_isspace (*ptr); ptr++) { /* Do Nothing */ }
name = ptr;
for (; *ptr && *ptr != ':'; ptr++) { /* Do Nothing */ }
*ptr = 0;
if (!(nd = find_device_by_name (self, name)))
nd = register_counters_by_name (self, name);
ptr++;
sscanf (ptr,
"%"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT
" %"G_GINT64_FORMAT,
&nd->rx_bytes,
&nd->rx_packets,
&nd->rx_errors,
&nd->rx_dropped,
&nd->rx_fifo,
&nd->rx_frame,
&nd->rx_compressed,
&nd->rx_multicast,
&nd->tx_bytes,
&nd->tx_packets,
&nd->tx_errors,
&nd->tx_dropped,
&nd->tx_fifo,
&nd->tx_collisions,
&nd->tx_carrier,
&nd->tx_compressed);
combined_rx += nd->rx_bytes;
combined_tx += nd->tx_bytes;
g_array_append_val (counters, nd->rx_bytes_id);
g_array_append_val (values, nd->rx_bytes);
g_array_append_val (counters, nd->tx_bytes_id);
g_array_append_val (values, nd->tx_bytes);
}
g_array_append_val (counters, self->combined_rx_id);
g_array_append_val (values, combined_rx);
g_array_append_val (counters, self->combined_tx_id);
g_array_append_val (values, combined_tx);
sysprof_capture_writer_set_counters (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
(const guint *)(gpointer)counters->data,
(const SysprofCaptureCounterValue *)(gpointer)values->data,
counters->len);
return G_SOURCE_CONTINUE;
}
static void
sysprof_netdev_source_start (SysprofSource *source)
{
SysprofNetdevSource *self = (SysprofNetdevSource *)source;
g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
self->poll_source = g_timeout_add (200, sysprof_netdev_source_poll_cb, self);
/* Poll immediately */
sysprof_netdev_source_poll_cb (self);
}
static void
sysprof_netdev_source_stop (SysprofSource *source)
{
SysprofNetdevSource *self = (SysprofNetdevSource *)source;
g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
/* Poll one last time */
sysprof_netdev_source_poll_cb (self);
g_clear_handle_id (&self->poll_source, g_source_remove);
sysprof_source_emit_finished (source);
}
static void
sysprof_netdev_source_set_writer (SysprofSource *source,
SysprofCaptureWriter *writer)
{
SysprofNetdevSource *self = (SysprofNetdevSource *)source;
g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
g_assert (writer != NULL);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
self->writer = sysprof_capture_writer_ref (writer);
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->get_is_ready = sysprof_netdev_source_get_is_ready;
iface->prepare = sysprof_netdev_source_prepare;
iface->set_writer = sysprof_netdev_source_set_writer;
iface->start = sysprof_netdev_source_start;
iface->stop = sysprof_netdev_source_stop;
}
static void
sysprof_netdev_source_finalize (GObject *object)
{
SysprofNetdevSource *self = (SysprofNetdevSource *)object;
g_clear_pointer (&self->netdevs, g_array_unref);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
if (self->netdev_fd != -1)
{
close (self->netdev_fd);
self->netdev_fd = -1;
}
G_OBJECT_CLASS (sysprof_netdev_source_parent_class)->finalize (object);
}
static void
sysprof_netdev_source_class_init (SysprofNetdevSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_netdev_source_finalize;
}
static void
sysprof_netdev_source_init (SysprofNetdevSource *self)
{
self->netdevs = g_array_new (FALSE, FALSE, sizeof (Netdev));
}
SysprofSource *
sysprof_netdev_source_new (void)
{
return g_object_new (SYSPROF_TYPE_NETDEV_SOURCE, NULL);
}

View File

@ -1,35 +0,0 @@
/* sysprof-netdev-source.h
*
* Copyright 2019 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
*/
#pragma once
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_NETDEV_SOURCE (sysprof_netdev_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofNetdevSource, sysprof_netdev_source, SYSPROF, NETDEV_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_netdev_source_new (void);
G_END_DECLS

View File

@ -1,554 +0,0 @@
/* sysprof-path-resolver.c
*
* Copyright 2021 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 <string.h>
#include "sysprof-path-resolver.h"
struct _SysprofPathResolver
{
GArray *mounts;
GArray *mountpoints;
};
typedef struct
{
/* The path on the host system */
char *on_host;
/* The path inside the process domain */
char *in_process;
/* The length of @in_process in bytes */
guint in_process_len;
/* The depth of the mount (for FUSE overlays) */
int depth;
} Mountpoint;
typedef struct
{
char *device;
char *mountpoint;
char *filesystem;
char *subvolid;
char *subvol;
} Mount;
typedef struct _st_mountinfo
{
char *id;
char *parent_id;
char *st_dev;
char *root;
char *mount_point;
char *mount_options;
char *filesystem;
char *mount_source;
char *super_options;
} st_mountinfo;
static void
clear_st_mountinfo (st_mountinfo *st)
{
g_free (st->id);
g_free (st->parent_id);
g_free (st->st_dev);
g_free (st->root);
g_free (st->mount_point);
g_free (st->mount_options);
g_free (st->filesystem);
g_free (st->mount_source);
g_free (st->super_options);
}
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (st_mountinfo, clear_st_mountinfo)
static void
clear_mount (Mount *m)
{
g_clear_pointer (&m->device, g_free);
g_clear_pointer (&m->mountpoint, g_free);
g_clear_pointer (&m->subvolid, g_free);
g_clear_pointer (&m->subvol, g_free);
g_clear_pointer (&m->filesystem, g_free);
}
static void
clear_mountpoint (Mountpoint *mp)
{
g_clear_pointer (&mp->on_host, g_free);
g_clear_pointer (&mp->in_process, g_free);
}
static gboolean
ignore_fs (const char *fs)
{
static gsize initialized;
static GHashTable *ignored;
if (g_once_init_enter (&initialized))
{
ignored = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_add (ignored, (char *)"autofs");
g_hash_table_add (ignored, (char *)"binfmt_misc");
g_hash_table_add (ignored, (char *)"bpf");
g_hash_table_add (ignored, (char *)"cgroup");
g_hash_table_add (ignored, (char *)"cgroup2");
g_hash_table_add (ignored, (char *)"configfs");
g_hash_table_add (ignored, (char *)"debugfs");
g_hash_table_add (ignored, (char *)"devpts");
g_hash_table_add (ignored, (char *)"devtmpfs");
g_hash_table_add (ignored, (char *)"efivarfs");
g_hash_table_add (ignored, (char *)"fusectl");
g_hash_table_add (ignored, (char *)"hugetlbfs");
g_hash_table_add (ignored, (char *)"mqueue");
g_hash_table_add (ignored, (char *)"none");
g_hash_table_add (ignored, (char *)"portal");
g_hash_table_add (ignored, (char *)"proc");
g_hash_table_add (ignored, (char *)"pstore");
g_hash_table_add (ignored, (char *)"ramfs");
g_hash_table_add (ignored, (char *)"rpc_pipefs");
g_hash_table_add (ignored, (char *)"securityfs");
g_hash_table_add (ignored, (char *)"selinuxfs");
g_hash_table_add (ignored, (char *)"sunrpc");
g_hash_table_add (ignored, (char *)"sysfs");
g_hash_table_add (ignored, (char *)"systemd-1");
g_hash_table_add (ignored, (char *)"tmpfs");
g_hash_table_add (ignored, (char *)"tracefs");
g_once_init_leave (&initialized, (gsize)1);
}
if (g_str_has_prefix (fs, "fuse."))
return TRUE;
return g_hash_table_contains (ignored, fs);
}
static char *
path_copy_with_trailing_slash (const char *path)
{
if (g_str_has_suffix (path, "/"))
return g_strdup (path);
else
return g_strdup_printf ("%s/", path);
}
static void
decode_space (char **str)
{
/* Replace encoded space "\040" with ' ' */
if (strstr (*str, "\\040") != NULL)
{
g_auto(GStrv) parts = g_strsplit (*str, "\\040", 0);
g_free (*str);
*str = g_strjoinv (" ", parts);
}
}
static char *
strdup_decode_space (const char *str)
{
char *copy = g_strdup (str);
decode_space (&copy);
return copy;
}
static gboolean
has_prefix_or_equal (const char *str,
const char *prefix)
{
return g_str_has_prefix (str, prefix) || strcmp (str, prefix) == 0;
}
static char *
get_option (const char *options,
const char *option)
{
g_auto(GStrv) parts = NULL;
g_assert (option != NULL);
g_assert (g_str_has_suffix (option, "="));
if (options == NULL)
return NULL;
parts = g_strsplit (options, ",", 0);
for (guint i = 0; parts[i] != NULL; i++)
{
if (g_str_has_prefix (parts[i], option))
{
const char *ret = parts[i] + strlen (option);
/* Easier to handle "" as NULL */
if (*ret == 0)
return NULL;
return g_strdup (ret);
}
}
return NULL;
}
static gboolean
parse_st_mountinfo (st_mountinfo *mi,
const char *line)
{
g_auto(GStrv) parts = NULL;
guint i;
g_assert (mi != NULL);
g_assert (line != NULL);
memset (mi, 0, sizeof *mi);
parts = g_strsplit (line, " ", 0);
if (g_strv_length (parts) < 10)
return FALSE;
mi->id = g_strdup (parts[0]);
mi->parent_id = g_strdup (parts[1]);
mi->st_dev = g_strdup (parts[2]);
mi->root = strdup_decode_space (parts[3]);
mi->mount_point = strdup_decode_space (parts[4]);
mi->mount_options = strdup_decode_space (parts[5]);
for (i = 6; parts[i] != NULL && !g_str_equal (parts[i], "-"); i++)
{
/* Do nothing. We just want to skip until after the optional tags
* section which is finished with " - ".
*/
}
/* Skip past - if there is anything */
if (parts[i] == NULL || parts[++i] == NULL)
return TRUE;
/* Get filesystem if provided */
mi->filesystem = g_strdup (parts[i++]);
if (parts[i] == NULL)
return TRUE;
/* Get mount source if provided */
mi->mount_source = strdup_decode_space (parts[i++]);
if (parts[i] == NULL)
return TRUE;
/* Get super options if provided */
mi->super_options = strdup_decode_space (parts[i++]);
if (parts[i] == NULL)
return TRUE;
/* Perhaps mountinfo will be extended once again ... */
return TRUE;
}
static void
parse_mounts (SysprofPathResolver *self,
const char *mounts)
{
g_auto(GStrv) lines = NULL;
g_assert (self != NULL);
g_assert (self->mounts != NULL);
g_assert (mounts != NULL);
lines = g_strsplit (mounts, "\n", 0);
for (guint i = 0; lines[i]; i++)
{
g_auto(GStrv) parts = g_strsplit (lines[i], " ", 5);
g_autofree char *subvolid = NULL;
g_autofree char *subvol = NULL;
const char *filesystem;
const char *mountpoint;
const char *device;
const char *options;
Mount m;
/* Field 0: device
* Field 1: mountpoint
* Field 2: filesystem
* Field 3: Options
* .. Ignored ..
*/
if (g_strv_length (parts) != 5)
continue;
filesystem = parts[2];
if (ignore_fs (filesystem))
continue;
for (guint j = 0; parts[j]; j++)
decode_space (&parts[j]);
device = parts[0];
mountpoint = parts[1];
options = parts[3];
if (g_strcmp0 (filesystem, "btrfs") == 0)
{
subvolid = get_option (options, "subvolid=");
subvol = get_option (options, "subvol=");
}
m.device = g_strdup (device);
m.filesystem = g_strdup (filesystem);
m.mountpoint = path_copy_with_trailing_slash (mountpoint);
m.subvolid = g_steal_pointer (&subvolid);
m.subvol = g_steal_pointer (&subvol);
g_array_append_val (self->mounts, m);
}
}
static const Mount *
find_mount (SysprofPathResolver *self,
const st_mountinfo *mi)
{
g_autofree char *subvolid = NULL;
g_assert (self != NULL);
g_assert (mi != NULL);
subvolid = get_option (mi->super_options, "subvolid=");
for (guint i = 0; i < self->mounts->len; i++)
{
const Mount *mount = &g_array_index (self->mounts, Mount, i);
if (g_strcmp0 (mount->device, mi->mount_source) == 0)
{
/* Sanity check that filesystems match */
if (g_strcmp0 (mount->filesystem, mi->filesystem) != 0)
continue;
/* If we have a subvolume (btrfs) make sure they match */
if (subvolid == NULL ||
g_strcmp0 (subvolid, mount->subvolid) == 0)
return mount;
}
}
return NULL;
}
static void
parse_mountinfo_line (SysprofPathResolver *self,
const char *line)
{
g_auto(st_mountinfo) st_mi = {0};
g_autofree char *subvol = NULL;
const char *path;
const Mount *mount;
Mountpoint mp = {0};
g_assert (self != NULL);
g_assert (self->mounts != NULL);
g_assert (self->mountpoints != NULL);
if (!parse_st_mountinfo (&st_mi, line))
return;
if (ignore_fs (st_mi.filesystem))
return;
if (!(mount = find_mount (self, &st_mi)))
return;
subvol = get_option (st_mi.super_options, "subvol=");
path = st_mi.root;
/* If the mount root has a prefix of the subvolume, then
* subtract that from the path (as we will resolve relative
* to the location where it is mounted via the Mount.mountpoint.
*/
if (subvol != NULL && has_prefix_or_equal (path, subvol))
{
path += strlen (subvol);
if (*path == 0)
path = NULL;
}
if (path == NULL)
{
mp.on_host = g_strdup (mount->mountpoint);
}
else
{
while (*path == '/')
path++;
mp.on_host = g_build_filename (mount->mountpoint, path, NULL);
}
if (g_str_has_suffix (mp.on_host, "/") &&
!g_str_has_suffix (st_mi.mount_point, "/"))
mp.in_process = g_build_filename (st_mi.mount_point, "/", NULL);
else
mp.in_process = g_strdup (st_mi.mount_point);
mp.in_process_len = strlen (mp.in_process);
mp.depth = -1;
g_array_append_val (self->mountpoints, mp);
}
static gint
sort_by_length (gconstpointer a,
gconstpointer b)
{
const Mountpoint *mpa = a;
const Mountpoint *mpb = b;
gsize alen = strlen (mpa->in_process);
gsize blen = strlen (mpb->in_process);
if (alen > blen)
return -1;
else if (blen > alen)
return 1;
if (mpa->depth < mpb->depth)
return -1;
else if (mpa->depth > mpb->depth)
return 1;
return 0;
}
static void
parse_mountinfo (SysprofPathResolver *self,
const char *mountinfo)
{
g_auto(GStrv) lines = NULL;
g_assert (self != NULL);
g_assert (self->mounts != NULL);
g_assert (self->mountpoints != NULL);
g_assert (mountinfo != NULL);
lines = g_strsplit (mountinfo, "\n", 0);
for (guint i = 0; lines[i]; i++)
parse_mountinfo_line (self, lines[i]);
g_array_sort (self->mountpoints, sort_by_length);
}
SysprofPathResolver *
_sysprof_path_resolver_new (const char *mounts,
const char *mountinfo)
{
SysprofPathResolver *self;
self = g_slice_new0 (SysprofPathResolver);
self->mounts = g_array_new (FALSE, FALSE, sizeof (Mount));
self->mountpoints = g_array_new (FALSE, FALSE, sizeof (Mountpoint));
g_array_set_clear_func (self->mounts, (GDestroyNotify)clear_mount);
g_array_set_clear_func (self->mountpoints, (GDestroyNotify)clear_mountpoint);
if (mounts != NULL)
parse_mounts (self, mounts);
if (mountinfo != NULL)
parse_mountinfo (self, mountinfo);
return self;
}
void
_sysprof_path_resolver_free (SysprofPathResolver *self)
{
g_clear_pointer (&self->mountpoints, g_array_unref);
g_clear_pointer (&self->mounts, g_array_unref);
g_slice_free (SysprofPathResolver, self);
}
void
_sysprof_path_resolver_add_overlay (SysprofPathResolver *self,
const char *in_process,
const char *on_host,
int depth)
{
Mountpoint mp;
g_return_if_fail (self != NULL);
g_return_if_fail (in_process != NULL);
g_return_if_fail (on_host != NULL);
mp.in_process = path_copy_with_trailing_slash (in_process);
mp.in_process_len = strlen (mp.in_process);
mp.on_host = path_copy_with_trailing_slash (on_host);
mp.depth = depth;
g_array_append_val (self->mountpoints, mp);
g_array_sort (self->mountpoints, sort_by_length);
}
char *
_sysprof_path_resolver_resolve (SysprofPathResolver *self,
const char *path)
{
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (path != NULL, NULL);
/* TODO: Cache the directory name of @path and use that for followup
* searches like we did in SysprofMountinfo.
*/
for (guint i = 0; i < self->mountpoints->len; i++)
{
const Mountpoint *mp = &g_array_index (self->mountpoints, Mountpoint, i);
if (g_str_has_prefix (path, mp->in_process))
{
g_autofree char *dst = g_build_filename (mp->on_host,
path + mp->in_process_len,
NULL);
/* If the depth is > -1, then we are dealing with an overlay. We
* unfortunately have to stat() to see if the file exists and then
* skip over this if not.
*
* TODO: This is going to break when we are recording from within
* flatpak as we'll not be able to stat files unless they are
* within the current users home, so system containers would
* be unlilkely to resolve.
*/
if (mp->depth > -1)
{
if (g_file_test (dst, G_FILE_TEST_EXISTS))
return g_steal_pointer (&dst);
continue;
}
return g_steal_pointer (&dst);
}
}
return NULL;
}

View File

@ -1,41 +0,0 @@
/* sysprof-path-resolver.h
*
* Copyright 2021 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
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
typedef struct _SysprofPathResolver SysprofPathResolver;
SysprofPathResolver *_sysprof_path_resolver_new (const char *mounts,
const char *mountinfo);
void _sysprof_path_resolver_add_overlay (SysprofPathResolver *self,
const char *in_process,
const char *on_host,
int depth);
void _sysprof_path_resolver_free (SysprofPathResolver *self);
char *_sysprof_path_resolver_resolve (SysprofPathResolver *self,
const char *path);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofPathResolver, _sysprof_path_resolver_free)
G_END_DECLS

View File

@ -1,504 +0,0 @@
/* sysprof-perf-counter.c
*
* Copyright 2016-2019 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
*/
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* Copyright 2004, 2005, Soeren Sandmann
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <errno.h>
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#ifdef HAVE_STDATOMIC_H
# include <stdatomic.h>
#endif
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "sysprof-helpers.h"
#include "sysprof-perf-counter.h"
/*
* Number of pages to map for the ring buffer. We map one additional buffer
* at the beginning for header information to communicate with perf.
*/
#define N_PAGES 32
/*
* This represents a stream coming to us from perf. All SysprofPerfCounterInfo
* share a single GSource used for watching incoming G_IO_IN requests.
* The map is the mmap() zone we are using as a ring buffer for communicating
* with perf. The rest is for managing the ring buffer.
*/
typedef struct
{
gint fd;
gpointer fdtag;
struct perf_event_mmap_page *map;
guint8 *data;
guint64 tail;
gint cpu;
guint in_callback : 1;
} SysprofPerfCounterInfo;
struct _SysprofPerfCounter
{
volatile gint ref_count;
/*
* If we are should currently be enabled. We allow calling
* multiple times and disabling when we reach zero.
*/
guint enabled;
/*
* Our main context and source for delivering callbacks.
*/
GMainContext *context;
GSource *source;
/*
* An array of SysprofPerfCounterInfo, indicating all of our open
* perf stream.s
*/
GPtrArray *info;
/*
* The callback to execute for every discovered perf event.
*/
SysprofPerfCounterCallback callback;
gpointer callback_data;
GDestroyNotify callback_data_destroy;
/*
* The number of samples we've recorded.
*/
guint64 n_samples;
};
typedef struct
{
GSource source;
SysprofPerfCounter *counter;
} PerfGSource;
G_DEFINE_BOXED_TYPE (SysprofPerfCounter,
sysprof_perf_counter,
(GBoxedCopyFunc)sysprof_perf_counter_ref,
(GBoxedFreeFunc)sysprof_perf_counter_unref)
static gboolean
perf_gsource_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
return callback ? callback (user_data) : G_SOURCE_CONTINUE;
}
static GSourceFuncs source_funcs = {
NULL, NULL, perf_gsource_dispatch, NULL
};
static void
sysprof_perf_counter_info_free (SysprofPerfCounterInfo *info)
{
if (info->map)
{
gsize map_size;
map_size = N_PAGES * getpagesize () + getpagesize ();
munmap (info->map, map_size);
info->map = NULL;
info->data = NULL;
}
if (info->fd != -1)
{
close (info->fd);
info->fd = 0;
}
g_slice_free (SysprofPerfCounterInfo, info);
}
static void
sysprof_perf_counter_finalize (SysprofPerfCounter *self)
{
guint i;
g_assert (self != NULL);
g_assert (self->ref_count == 0);
for (i = 0; i < self->info->len; i++)
{
SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i);
if (info->fdtag)
g_source_remove_unix_fd (self->source, info->fdtag);
sysprof_perf_counter_info_free (info);
}
if (self->callback_data_destroy)
self->callback_data_destroy (self->callback_data);
g_clear_pointer (&self->source, g_source_destroy);
g_clear_pointer (&self->info, g_ptr_array_unref);
g_clear_pointer (&self->context, g_main_context_unref);
g_slice_free (SysprofPerfCounter, self);
}
void
sysprof_perf_counter_unref (SysprofPerfCounter *self)
{
g_return_if_fail (self != NULL);
g_return_if_fail (self->ref_count > 0);
if (g_atomic_int_dec_and_test (&self->ref_count))
sysprof_perf_counter_finalize (self);
}
SysprofPerfCounter *
sysprof_perf_counter_ref (SysprofPerfCounter *self)
{
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (self->ref_count > 0, NULL);
g_atomic_int_inc (&self->ref_count);
return self;
}
static void
sysprof_perf_counter_flush (SysprofPerfCounter *self,
SysprofPerfCounterInfo *info)
{
guint64 head;
guint64 tail;
guint64 n_bytes = N_PAGES * getpagesize ();
guint64 mask = n_bytes - 1;
g_assert (self != NULL);
g_assert (info != NULL);
tail = info->tail;
head = info->map->data_head;
#ifdef HAVE_STDATOMIC_H
atomic_thread_fence (memory_order_acquire);
#elif G_GNUC_CHECK_VERSION(3, 0)
__sync_synchronize ();
#endif
if (head < tail)
tail = head;
while ((head - tail) >= sizeof (struct perf_event_header))
{
g_autofree guint8 *free_me = NULL;
struct perf_event_header *header;
guint8 buffer[4096];
/* Note that:
*
* - perf events are a multiple of 64 bits
* - the perf event header is 64 bits
* - the data area is a multiple of 64 bits
*
* which means there will always be space for one header, which means we
* can safely dereference the size field.
*/
header = (struct perf_event_header *)(gpointer)(info->data + (tail & mask));
if (header->size > head - tail)
{
/* The kernel did not generate a complete event.
* I don't think that can happen, but we may as well
* be paranoid.
*/
break;
}
if (info->data + (tail & mask) + header->size > info->data + n_bytes)
{
gint n_before;
gint n_after;
guint8 *b;
if (header->size > sizeof buffer)
free_me = b = g_malloc (header->size);
else
b = buffer;
n_after = (tail & mask) + header->size - n_bytes;
n_before = header->size - n_after;
memcpy (b, info->data + (tail & mask), n_before);
memcpy (b + n_before, info->data, n_after);
header = (struct perf_event_header *)(gpointer)b;
}
if (header->type == PERF_RECORD_SAMPLE)
self->n_samples++;
if (self->callback != NULL)
{
info->in_callback = TRUE;
self->callback ((SysprofPerfCounterEvent *)header, info->cpu, self->callback_data);
info->in_callback = FALSE;
}
tail += header->size;
}
info->tail = tail;
#ifdef HAVE_STDATOMIC_H
atomic_thread_fence (memory_order_seq_cst);
#elif G_GNUC_CHECK_VERSION(3, 0)
__sync_synchronize ();
#endif
info->map->data_tail = tail;
}
static gboolean
sysprof_perf_counter_dispatch (gpointer user_data)
{
SysprofPerfCounter *self = user_data;
g_assert (self != NULL);
g_assert (self->info != NULL);
for (guint i = 0; i < self->info->len; i++)
{
SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i);
sysprof_perf_counter_flush (self, info);
}
return G_SOURCE_CONTINUE;
}
static void
sysprof_perf_counter_enable_info (SysprofPerfCounter *self,
SysprofPerfCounterInfo *info)
{
g_assert (self != NULL);
g_assert (info != NULL);
if (0 != ioctl (info->fd, PERF_EVENT_IOC_ENABLE))
g_warning ("Failed to enable counters");
g_source_modify_unix_fd (self->source, info->fdtag, G_IO_IN);
}
SysprofPerfCounter *
sysprof_perf_counter_new (GMainContext *context)
{
SysprofPerfCounter *ret;
if (context == NULL)
context = g_main_context_default ();
ret = g_slice_new0 (SysprofPerfCounter);
ret->ref_count = 1;
ret->info = g_ptr_array_new ();
ret->context = g_main_context_ref (context);
ret->source = g_source_new (&source_funcs, sizeof (PerfGSource));
((PerfGSource *)ret->source)->counter = ret;
g_source_set_callback (ret->source, sysprof_perf_counter_dispatch, ret, NULL);
g_source_set_name (ret->source, "[perf]");
g_source_attach (ret->source, context);
return ret;
}
void
sysprof_perf_counter_close (SysprofPerfCounter *self,
gint fd)
{
guint i;
g_return_if_fail (self != NULL);
g_return_if_fail (fd != -1);
for (i = 0; i < self->info->len; i++)
{
SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i);
if (info->fd == fd)
{
g_ptr_array_remove_index (self->info, i);
if (self->source)
g_source_remove_unix_fd (self->source, info->fdtag);
sysprof_perf_counter_info_free (info);
return;
}
}
}
static void
sysprof_perf_counter_add_info (SysprofPerfCounter *self,
int fd,
int cpu)
{
SysprofPerfCounterInfo *info;
guint8 *map;
gsize map_size;
g_assert (self != NULL);
g_assert (fd != -1);
map_size = N_PAGES * getpagesize () + getpagesize ();
map = mmap (NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED)
{
close (fd);
return;
}
info = g_slice_new0 (SysprofPerfCounterInfo);
info->fd = fd;
info->map = (gpointer)map;
info->data = map + getpagesize ();
info->tail = 0;
info->cpu = cpu;
g_ptr_array_add (self->info, info);
info->fdtag = g_source_add_unix_fd (self->source, info->fd, G_IO_ERR);
if (self->enabled)
sysprof_perf_counter_enable_info (self, info);
}
void
sysprof_perf_counter_take_fd (SysprofPerfCounter *self,
int fd)
{
g_return_if_fail (self != NULL);
g_return_if_fail (fd > -1);
sysprof_perf_counter_add_info (self, fd, -1);
}
gint
sysprof_perf_counter_open (SysprofPerfCounter *self,
struct perf_event_attr *attr,
GPid pid,
gint cpu,
gint group_fd,
gulong flags)
{
SysprofHelpers *helpers = sysprof_helpers_get_default ();
gint out_fd = -1;
g_return_val_if_fail (self != NULL, -1);
g_return_val_if_fail (attr != NULL, -1);
g_return_val_if_fail (cpu >= -1, -1);
g_return_val_if_fail (pid >= -1, -1);
g_return_val_if_fail (group_fd >= -1, -1);
if (sysprof_helpers_perf_event_open (helpers, attr, pid, cpu, group_fd, flags, NULL, &out_fd, NULL))
{
sysprof_perf_counter_take_fd (self, out_fd);
return out_fd;
}
return -1;
}
void
sysprof_perf_counter_set_callback (SysprofPerfCounter *self,
SysprofPerfCounterCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy)
{
g_return_if_fail (self != NULL);
if (self->callback_data_destroy)
self->callback_data_destroy (self->callback_data);
self->callback = callback;
self->callback_data = callback_data;
self->callback_data_destroy = callback_data_destroy;
}
void
sysprof_perf_counter_enable (SysprofPerfCounter *self)
{
g_return_if_fail (self != NULL);
if (g_atomic_int_add (&self->enabled, 1) == 0)
{
for (guint i = 0; i < self->info->len; i++)
{
SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i);
sysprof_perf_counter_enable_info (self, info);
}
}
}
void
sysprof_perf_counter_disable (SysprofPerfCounter *self)
{
g_return_if_fail (self != NULL);
if (g_atomic_int_dec_and_test (&self->enabled))
{
for (guint i = 0; i < self->info->len; i++)
{
SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i);
if (0 != ioctl (info->fd, PERF_EVENT_IOC_DISABLE))
g_warning ("Failed to disable counters");
if (!info->in_callback)
sysprof_perf_counter_flush (self, info);
g_source_modify_unix_fd (self->source, info->fdtag, G_IO_ERR);
}
}
}

View File

@ -1,151 +0,0 @@
/* sysprof-perf-counter.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include <gio/gio.h>
#include <linux/perf_event.h>
G_BEGIN_DECLS
/* Structs representing the layouts of perf records returned by the
* kernel.
*
* perf returns variable-layout structs based on the
* perf_event_sample_format selectors in perf_event_attr.sample_type.
* These structs are the particular layouts that sysprof requests.
*/
#define SYSPROF_TYPE_PERF_COUNTER (sysprof_perf_counter_get_type())
typedef struct _SysprofPerfCounter SysprofPerfCounter;
#pragma pack(push, 1)
typedef struct
{
struct perf_event_header header;
guint32 pid;
guint32 ppid;
guint32 tid;
guint32 ptid;
guint64 time;
} SysprofPerfCounterEventFork;
typedef struct
{
struct perf_event_header header;
guint32 pid;
guint32 tid;
gchar comm[0];
} SysprofPerfCounterEventComm;
typedef struct
{
struct perf_event_header header;
guint32 pid;
guint32 ppid;
guint32 tid;
guint32 ptid;
guint64 time;
} SysprofPerfCounterEventExit;
typedef struct
{
struct perf_event_header header;
guint32 pid;
guint32 tid;
guint64 addr;
guint64 len;
guint64 pgoff;
char filename[0];
} SysprofPerfCounterEventMmap;
typedef struct
{
struct perf_event_header header;
guint64 identifier;
guint64 ip;
guint32 pid;
guint32 tid;
guint64 time;
guint64 n_ips;
guint64 ips[0];
} SysprofPerfCounterEventCallchain;
typedef struct
{
struct perf_event_header header;
guint64 identifier;
guint64 ip;
guint32 pid;
guint32 tid;
guint64 time;
guint32 raw_size;
guchar raw[];
} SysprofPerfCounterEventTracepoint;
typedef union
{
struct perf_event_header header;
guint8 raw[0];
SysprofPerfCounterEventFork fork;
SysprofPerfCounterEventComm comm;
SysprofPerfCounterEventExit exit;
SysprofPerfCounterEventMmap mmap;
SysprofPerfCounterEventCallchain callchain;
SysprofPerfCounterEventTracepoint tracepoint;
} SysprofPerfCounterEvent;
#pragma pack(pop)
typedef void (*SysprofPerfCounterCallback) (SysprofPerfCounterEvent *event,
guint cpu,
gpointer user_data);
GType sysprof_perf_counter_get_type (void);
SysprofPerfCounter *sysprof_perf_counter_new (GMainContext *context);
void sysprof_perf_counter_set_callback (SysprofPerfCounter *self,
SysprofPerfCounterCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy);
void sysprof_perf_counter_add_pid (SysprofPerfCounter *self,
GPid pid);
gint sysprof_perf_counter_open (SysprofPerfCounter *self,
struct perf_event_attr *attr,
GPid pid,
gint cpu,
gint group_fd,
gulong flags);
void sysprof_perf_counter_take_fd (SysprofPerfCounter *self,
int fd);
void sysprof_perf_counter_enable (SysprofPerfCounter *self);
void sysprof_perf_counter_disable (SysprofPerfCounter *self);
void sysprof_perf_counter_close (SysprofPerfCounter *self,
gint fd);
SysprofPerfCounter *sysprof_perf_counter_ref (SysprofPerfCounter *self);
void sysprof_perf_counter_unref (SysprofPerfCounter *self);
G_END_DECLS

View File

@ -1,831 +0,0 @@
/* sysprof-perf-source.c
*
* Copyright 2016-2019 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
*/
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* Copyright 2004, 2005, Soeren Sandmann
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <gio/gio.h>
#include <glib/gi18n.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#include "sysprof-clock.h"
#include "sysprof-helpers.h"
#include "sysprof-line-reader.h"
#include "sysprof-perf-counter.h"
#include "sysprof-perf-source.h"
#define N_WAKEUP_EVENTS 149
/* Identifiers for the various tracepoints we might watch for */
enum SysprofTracepoint
{
DRM_VBLANK,
DRM_I915_BEGIN,
DRM_I915_END,
};
typedef struct
{
enum SysprofTracepoint tp;
const char *path;
const char **fields;
} SysprofOptionalTracepoint;
/* Global list of the optional tracepoints we might want to watch. */
static const SysprofOptionalTracepoint optional_tracepoints[] = {
/* This event fires just after the vblank IRQ handler starts.
*
* Note that on many platforms when nothing is waiting for vblank
* (no pageflips have happened recently, no rendering is
* synchronizing to vblank), the vblank IRQ will get masked off and
* the event won't show up in the timeline.
*
* Also note that when we're in watch-a-single-process mode, we
* won't get the event since it comes in on an IRQ handler, not for
* our pid.
*/
{ DRM_VBLANK, "drm/drm_vblank_event",
(const char *[]){ "crtc", "seq", NULL } },
/* I915 GPU execution.
*
* These are the wrong events to be watching. We need to use the
* ones under CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS instead.
*/
#if 0
{ DRM_I915_BEGIN, "i915/i915_gem_request_add",
(const char *[]){ "ctx", "ring", "seqno", NULL } },
{ DRM_I915_END, "i915/i915_gem_request_retire",
(const char *[]){ "ctx", "ring", "seqno", NULL } },
#endif
};
/* Struct describing tracepoint events.
*
* This should be extended with some sort of union for the describing
* the locations of the relevant fields within the _RAW section of the
* struct perf_event, so we can pick out things like the vblank CRTC
* number and MSC.
*/
typedef struct {
enum SysprofTracepoint tp;
gsize field_offsets[0];
} SysprofTracepointDesc;
struct _SysprofPerfSource
{
GObject parent_instance;
SysprofCaptureWriter *writer;
SysprofPerfCounter *counter;
GHashTable *pids;
/* Mapping from perf sample identifiers to SysprofTracepointDesc. */
GHashTable *tracepoint_event_ids;
guint running : 1;
guint is_ready : 1;
};
static void source_iface_init (SysprofSourceInterface *iface);
G_DEFINE_TYPE_EXTENDED (SysprofPerfSource, sysprof_perf_source, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
enum {
TARGET_EXITED,
N_SIGNALS
};
static guint signals [N_SIGNALS];
static void
sysprof_perf_source_real_target_exited (SysprofPerfSource *self)
{
g_assert (SYSPROF_IS_PERF_SOURCE (self));
sysprof_source_emit_finished (SYSPROF_SOURCE (self));
}
static void
sysprof_perf_source_finalize (GObject *object)
{
SysprofPerfSource *self = (SysprofPerfSource *)object;
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
g_clear_pointer (&self->counter, sysprof_perf_counter_unref);
g_clear_pointer (&self->pids, g_hash_table_unref);
g_clear_pointer (&self->tracepoint_event_ids, g_hash_table_unref);
G_OBJECT_CLASS (sysprof_perf_source_parent_class)->finalize (object);
}
static void
sysprof_perf_source_class_init (SysprofPerfSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_perf_source_finalize;
signals [TARGET_EXITED] =
g_signal_new_class_handler ("target-exited",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_CALLBACK (sysprof_perf_source_real_target_exited),
NULL, NULL, NULL, G_TYPE_NONE, 0);
}
static void
sysprof_perf_source_init (SysprofPerfSource *self)
{
self->pids = g_hash_table_new (NULL, NULL);
self->tracepoint_event_ids = g_hash_table_new (NULL, NULL);
}
static gboolean
do_emit_exited (gpointer data)
{
g_autoptr(SysprofPerfSource) self = data;
g_signal_emit (self, signals [TARGET_EXITED], 0);
return G_SOURCE_REMOVE;
}
static void
sysprof_perf_source_handle_tracepoint (SysprofPerfSource *self,
gint cpu,
const SysprofPerfCounterEventTracepoint *sample,
SysprofTracepointDesc *tp_desc)
{
gchar *message = NULL;
/* Note that field_offsets[] correspond to the
* SysprofOptionalTracepoint->fields[] strings. Yes, this is gross.
*/
switch (tp_desc->tp)
{
case DRM_VBLANK:
message = g_strdup_printf ("crtc=%d, seq=%u",
*(gint *)(gpointer)(sample->raw + tp_desc->field_offsets[0]),
*(guint *)(gpointer)(sample->raw + tp_desc->field_offsets[1]));
sysprof_capture_writer_add_mark (self->writer,
sample->time,
cpu,
sample->pid,
0,
"drm",
"vblank",
message);
break;
case DRM_I915_BEGIN:
case DRM_I915_END:
message = g_strdup_printf ("ctx=%u, ring=%u, seqno=%u",
*(guint *)(gpointer)(sample->raw + tp_desc->field_offsets[0]),
*(guint *)(gpointer)(sample->raw + tp_desc->field_offsets[1]),
*(guint *)(gpointer)(sample->raw + tp_desc->field_offsets[2]));
sysprof_capture_writer_add_mark (self->writer,
sample->time,
cpu,
sample->pid,
0,
"drm",
(tp_desc->tp == DRM_I915_BEGIN ?
"i915 gpu begin" : "i915 gpu end"),
message);
break;
default:
break;
}
g_free (message);
}
static void
sysprof_perf_source_handle_callchain (SysprofPerfSource *self,
gint cpu,
const SysprofPerfCounterEventCallchain *sample)
{
const guint64 *ips;
gint n_ips;
guint64 trace[3];
g_assert (SYSPROF_IS_PERF_SOURCE (self));
g_assert (sample != NULL);
ips = sample->ips;
n_ips = sample->n_ips;
if (n_ips == 0)
{
if (sample->header.misc & PERF_RECORD_MISC_KERNEL)
{
trace[0] = PERF_CONTEXT_KERNEL;
trace[1] = sample->ip;
trace[2] = PERF_CONTEXT_USER;
ips = trace;
n_ips = 3;
}
else
{
trace[0] = PERF_CONTEXT_USER;
trace[1] = sample->ip;
ips = trace;
n_ips = 2;
}
}
sysprof_capture_writer_add_sample (self->writer,
sample->time,
cpu,
sample->pid,
sample->tid,
ips,
n_ips);
}
static inline void
realign (gsize *pos,
gsize align)
{
*pos = (*pos + align - 1) & ~(align - 1);
}
static void
sysprof_perf_source_handle_event (SysprofPerfCounterEvent *event,
guint cpu,
gpointer user_data)
{
SysprofPerfSource *self = user_data;
SysprofTracepointDesc *tp_desc;
gsize offset;
gint64 time;
g_assert (SYSPROF_IS_PERF_SOURCE (self));
g_assert (event != NULL);
switch (event->header.type)
{
case PERF_RECORD_COMM:
offset = strlen (event->comm.comm) + 1;
realign (&offset, sizeof (guint64));
offset += sizeof (GPid) + sizeof (GPid);
memcpy (&time, event->comm.comm + offset, sizeof time);
if (event->comm.pid == event->comm.tid)
sysprof_capture_writer_add_process (self->writer,
time,
cpu,
event->comm.pid,
event->comm.comm);
break;
case PERF_RECORD_EXIT:
/* Ignore fork exits for now */
if (event->exit.tid != event->exit.pid)
break;
sysprof_capture_writer_add_exit (self->writer,
event->exit.time,
cpu,
event->exit.pid);
if (g_hash_table_contains (self->pids, GINT_TO_POINTER (event->exit.pid)))
{
g_hash_table_remove (self->pids, GINT_TO_POINTER (event->exit.pid));
if (self->running && (g_hash_table_size (self->pids) == 0))
{
self->running = FALSE;
sysprof_perf_counter_disable (self->counter);
g_timeout_add (0, do_emit_exited, g_object_ref (self));
}
}
break;
case PERF_RECORD_FORK:
sysprof_capture_writer_add_fork (self->writer,
event->fork.time,
cpu,
event->fork.ptid,
event->fork.tid);
/*
* TODO: We should add support for "follow fork" of the GPid if we are
* targetting it.
*/
break;
case PERF_RECORD_LOST:
break;
case PERF_RECORD_MMAP:
offset = strlen (event->mmap.filename) + 1;
realign (&offset, sizeof (guint64));
offset += sizeof (GPid) + sizeof (GPid);
memcpy (&time, event->mmap.filename + offset, sizeof time);
sysprof_capture_writer_add_map (self->writer,
time,
cpu,
event->mmap.pid,
event->mmap.addr,
event->mmap.addr + event->mmap.len,
event->mmap.pgoff,
0,
event->mmap.filename);
break;
case PERF_RECORD_READ:
break;
case PERF_RECORD_SAMPLE:
/* We don't capture IPs with tracepoints, and get _RAW data
* instead. Handle them separately.
*/
g_assert (&event->callchain.identifier == &event->tracepoint.identifier);
tp_desc = g_hash_table_lookup (self->tracepoint_event_ids,
GINT_TO_POINTER (event->callchain.identifier));
if (tp_desc)
{
sysprof_perf_source_handle_tracepoint (self, cpu, &event->tracepoint, tp_desc);
}
else
{
sysprof_perf_source_handle_callchain (self, cpu, &event->callchain);
}
break;
case PERF_RECORD_THROTTLE:
case PERF_RECORD_UNTHROTTLE:
default:
break;
}
}
static gboolean
sysprof_perf_get_tracepoint_config (const char *path,
gint64 *config)
{
g_autofree gchar *filename = NULL;
g_autofree gchar *contents = NULL;
gsize len;
filename = g_strdup_printf ("/sys/kernel/debug/tracing/events/%s/id", path);
if (!g_file_get_contents (filename, &contents, &len, NULL))
return FALSE;
*config = g_ascii_strtoull (contents, NULL, 10);
return TRUE;
}
static gboolean
sysprof_perf_get_tracepoint_fields (SysprofTracepointDesc *tp_desc,
const SysprofOptionalTracepoint *optional_tp,
GError **error)
{
gchar *filename = NULL;
gchar *contents;
size_t len;
gint i;
filename = g_strdup_printf ("/sys/kernel/debug/tracing/events/%s/format",
optional_tp->path);
if (!filename)
return FALSE;
if (!g_file_get_contents (filename, &contents, &len, NULL))
{
g_free (filename);
return FALSE;
}
g_free (filename);
/* Look up our fields. Some example strings:
*
* field:unsigned short common_type; offset:0; size:2; signed:0;
* field:int crtc; offset:8; size:4; signed:1;
* field:unsigned int seq; offset:12; size:4; signed:0;
*/
for (i = 0; optional_tp->fields[i] != NULL; i++)
{
gchar *pattern = g_strdup_printf ("%s;\toffset:", optional_tp->fields[i]);
gchar *match;
gint64 offset;
match = strstr (contents, pattern);
if (!match)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
_("Sysprof failed to find field “%s”."),
optional_tp->fields[i]);
g_free (contents);
return FALSE;
}
offset = g_ascii_strtoll (match + strlen (pattern),
NULL, 0);
if (offset == G_MININT64 && errno != 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
_("Sysprof failed to parse offset for “%s”."),
optional_tp->fields[i]);
g_free (contents);
return FALSE;
}
tp_desc->field_offsets[i] = offset;
g_free (pattern);
}
g_free (contents);
return TRUE;
}
/* Adds a perf tracepoint event, if it's available.
*
* These are kernel tracepoints that we want to include in our capture
* when present, but may be kernel version or driver-specific.
*/
static void
sysprof_perf_source_add_optional_tracepoint (SysprofPerfSource *self,
GPid pid,
gint cpu,
const SysprofOptionalTracepoint *optional_tracepoint,
GError **error)
{
struct perf_event_attr attr = { 0 };
SysprofTracepointDesc *tp_desc;
gulong flags = 0;
gint fd;
gint64 config;
gint64 id;
int ret;
gint num_fields;
if (!sysprof_perf_get_tracepoint_config(optional_tracepoint->path, &config))
return;
attr.type = PERF_TYPE_TRACEPOINT;
attr.sample_type = PERF_SAMPLE_RAW
| PERF_SAMPLE_IP
| PERF_SAMPLE_TID
| PERF_SAMPLE_IDENTIFIER
| PERF_SAMPLE_RAW
| PERF_SAMPLE_TIME;
attr.config = config;
attr.sample_period = 1;
#ifdef HAVE_PERF_CLOCKID
attr.clockid = sysprof_clock;
attr.use_clockid = 1;
#endif
attr.size = sizeof attr;
fd = sysprof_perf_counter_open (self->counter, &attr, pid, cpu, -1, flags);
ret = ioctl (fd, PERF_EVENT_IOC_ID, &id);
if (ret != 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
_("Sysprof failed to get perf_event ID."));
close(fd);
return;
}
/* The fields list is NULL-terminated, count how many are there. */
for (num_fields = 0; optional_tracepoint->fields[num_fields]; num_fields++)
;
tp_desc = g_malloc (sizeof (*tp_desc) +
sizeof(*tp_desc->field_offsets) * num_fields);
if (!tp_desc)
{
close(fd);
return;
}
tp_desc->tp = optional_tracepoint->tp;
if (!sysprof_perf_get_tracepoint_fields (tp_desc, optional_tracepoint, error))
{
free(tp_desc);
close(fd);
return;
}
/* Here's where we should inspect the /format file to determine how
* to pick fields out of the _RAW data.
*/
/* We're truncating the event ID from 64b to 32 to fit in the hash.
* The event IDs start from 0 at boot, so meh.
*/
g_assert (id <= 0xffffffff);
g_hash_table_insert (self->tracepoint_event_ids, GINT_TO_POINTER (id), tp_desc);
}
static gboolean
sysprof_perf_source_start_pid (SysprofPerfSource *self,
GPid pid,
GError **error)
{
struct perf_event_attr attr = { 0 };
gulong flags = 0;
gint ncpu = g_get_num_processors ();
gint cpu = 0;
gint fd = -1;
g_assert (SYSPROF_IS_PERF_SOURCE (self));
attr.sample_type = PERF_SAMPLE_IP
| PERF_SAMPLE_TID
| PERF_SAMPLE_IDENTIFIER
| PERF_SAMPLE_CALLCHAIN
| PERF_SAMPLE_TIME;
attr.wakeup_events = N_WAKEUP_EVENTS;
attr.disabled = TRUE;
attr.mmap = 1;
attr.comm = 1;
attr.task = 1;
attr.exclude_idle = 1;
attr.sample_id_all = 1;
#ifdef HAVE_PERF_CLOCKID
attr.clockid = sysprof_clock;
attr.use_clockid = 1;
#endif
attr.size = sizeof attr;
if (pid != -1)
{
ncpu = 0;
cpu = -1;
}
/* Perf won't let us capture on all CPUs on all pids, so we have to
* loop over CPUs if we're not just watching a single pid.
*/
for (; cpu < ncpu; cpu++)
{
attr.type = PERF_TYPE_HARDWARE;
attr.config = PERF_COUNT_HW_CPU_CYCLES;
attr.sample_period = 1200000;
fd = sysprof_perf_counter_open (self->counter, &attr, pid, cpu, -1, flags);
if (fd == -1)
{
/*
* We might just not have access to hardware counters, so try to
* gracefully fallback to software counters.
*/
attr.type = PERF_TYPE_SOFTWARE;
attr.config = PERF_COUNT_SW_CPU_CLOCK;
attr.sample_period = 1000000;
errno = 0;
fd = sysprof_perf_counter_open (self->counter, &attr, pid, cpu, -1, flags);
if (fd == -1)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
_("An error occurred while attempting to access performance counters"));
sysprof_source_stop (SYSPROF_SOURCE (self));
return FALSE;
}
}
for (guint i = 0; i < G_N_ELEMENTS(optional_tracepoints); i++)
sysprof_perf_source_add_optional_tracepoint (self, pid, cpu,
&optional_tracepoints[i],
error);
}
return TRUE;
}
static void
sysprof_perf_source_start (SysprofSource *source)
{
SysprofPerfSource *self = (SysprofPerfSource *)source;
g_autoptr(GError) error = NULL;
g_assert (SYSPROF_IS_PERF_SOURCE (self));
self->counter = sysprof_perf_counter_new (NULL);
sysprof_perf_counter_set_callback (self->counter,
sysprof_perf_source_handle_event,
self, NULL);
if (g_hash_table_size (self->pids) > 0)
{
GHashTableIter iter;
gpointer key;
g_hash_table_iter_init (&iter, self->pids);
while (g_hash_table_iter_next (&iter, &key, NULL))
{
GPid pid = GPOINTER_TO_INT (key);
if (!sysprof_perf_source_start_pid (self, pid, &error))
{
sysprof_source_emit_failed (source, error);
return;
}
}
}
else
{
if (!sysprof_perf_source_start_pid (self, -1, &error))
{
sysprof_source_emit_failed (source, error);
return;
}
}
self->running = TRUE;
sysprof_perf_counter_enable (self->counter);
}
static void
sysprof_perf_source_stop (SysprofSource *source)
{
SysprofPerfSource *self = (SysprofPerfSource *)source;
g_assert (SYSPROF_IS_PERF_SOURCE (self));
if (self->running)
{
self->running = FALSE;
sysprof_perf_counter_disable (self->counter);
}
g_clear_pointer (&self->counter, sysprof_perf_counter_unref);
sysprof_source_emit_finished (source);
}
static void
sysprof_perf_source_set_writer (SysprofSource *source,
SysprofCaptureWriter *writer)
{
SysprofPerfSource *self = (SysprofPerfSource *)source;
g_assert (SYSPROF_IS_PERF_SOURCE (self));
g_assert (writer != NULL);
self->writer = sysprof_capture_writer_ref (writer);
}
static void
sysprof_perf_source_add_pid (SysprofSource *source,
GPid pid)
{
SysprofPerfSource *self = (SysprofPerfSource *)source;
g_return_if_fail (SYSPROF_IS_PERF_SOURCE (self));
g_return_if_fail (pid >= -1);
g_return_if_fail (self->writer == NULL);
g_hash_table_add (self->pids, GINT_TO_POINTER (pid));
}
static void
sysprof_perf_source_auth_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofHelpers *helpers = (SysprofHelpers *)object;
g_autoptr(SysprofPerfSource) self = user_data;
g_autoptr(GError) error = NULL;
g_assert (SYSPROF_IS_HELPERS (helpers));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_PERF_SOURCE (self));
if (!sysprof_helpers_authorize_finish (helpers, result, &error))
{
sysprof_source_emit_failed (SYSPROF_SOURCE (self), error);
}
else
{
self->is_ready = TRUE;
sysprof_source_emit_ready (SYSPROF_SOURCE (self));
}
}
static void
sysprof_perf_source_prepare (SysprofSource *source)
{
g_assert (SYSPROF_IS_PERF_SOURCE (source));
sysprof_helpers_authorize_async (sysprof_helpers_get_default (),
NULL,
sysprof_perf_source_auth_cb,
g_object_ref (source));
}
static gboolean
sysprof_perf_source_get_is_ready (SysprofSource *source)
{
return SYSPROF_PERF_SOURCE (source)->is_ready;
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->start = sysprof_perf_source_start;
iface->stop = sysprof_perf_source_stop;
iface->set_writer = sysprof_perf_source_set_writer;
iface->add_pid = sysprof_perf_source_add_pid;
iface->prepare = sysprof_perf_source_prepare;
iface->get_is_ready = sysprof_perf_source_get_is_ready;
}
SysprofSource *
sysprof_perf_source_new (void)
{
return g_object_new (SYSPROF_TYPE_PERF_SOURCE, NULL);
}
void
sysprof_perf_source_set_target_pid (SysprofPerfSource *self,
GPid pid)
{
g_return_if_fail (SYSPROF_IS_PERF_SOURCE (self));
g_return_if_fail (pid >= -1);
if (pid == -1)
g_hash_table_remove_all (self->pids);
else
sysprof_perf_source_add_pid (SYSPROF_SOURCE (self), pid);
}

View File

@ -1,43 +0,0 @@
/* sysprof-perf-source.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-source.h"
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_PERF_SOURCE (sysprof_perf_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofPerfSource, sysprof_perf_source, SYSPROF, PERF_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_perf_source_new (void);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_perf_source_set_target_pid (SysprofPerfSource *self,
GPid pid);
G_END_DECLS

View File

@ -1,318 +0,0 @@
/* sysprof-podman.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-podman"
#include "config.h"
#include <json-glib/json-glib.h>
#include "sysprof-podman.h"
static const char *debug_dirs[] = {
"/usr/lib/debug",
"/usr/lib32/debug",
"/usr/lib64/debug",
};
void
_sysprof_podman_debug_dirs (GPtrArray *dirs)
{
g_autofree gchar *base_path = NULL;
g_autoptr(GDir) dir = NULL;
const gchar *name;
g_assert (dirs != NULL);
base_path = g_build_filename (g_get_user_data_dir (),
"containers",
"storage",
"overlay",
NULL);
if (!(dir = g_dir_open (base_path, 0, NULL)))
return;
while ((name = g_dir_read_name (dir)))
{
for (guint i = 0; i < G_N_ELEMENTS (debug_dirs); i++)
{
g_autofree gchar *debug_path = g_build_filename (base_path, name, "diff", debug_dirs[i], NULL);
if (g_file_test (debug_path, G_FILE_TEST_IS_DIR))
g_ptr_array_add (dirs, g_steal_pointer (&debug_path));
}
}
}
gchar **
sysprof_podman_debug_dirs (void)
{
GPtrArray *dirs = g_ptr_array_new ();
_sysprof_podman_debug_dirs (dirs);
g_ptr_array_add (dirs, NULL);
return (gchar **)g_ptr_array_free (dirs, FALSE);
}
struct _SysprofPodman
{
JsonParser *containers_parser;
JsonParser *layers_parser;
JsonParser *images_parser;
};
void
sysprof_podman_free (SysprofPodman *self)
{
g_clear_object (&self->containers_parser);
g_clear_object (&self->layers_parser);
g_clear_object (&self->images_parser);
g_slice_free (SysprofPodman, self);
}
static void
load_containers (SysprofPodman *self)
{
g_autofree char *path = NULL;
g_assert (self != NULL);
path = g_build_filename (g_get_user_data_dir (),
"containers",
"storage",
"overlay-containers",
"containers.json",
NULL);
json_parser_load_from_file (self->containers_parser, path, NULL);
}
static void
load_layers (SysprofPodman *self)
{
g_autofree char *path = NULL;
g_assert (self != NULL);
path = g_build_filename (g_get_user_data_dir (),
"containers",
"storage",
"overlay-layers",
"layers.json",
NULL);
json_parser_load_from_file (self->layers_parser, path, NULL);
}
static void
load_images (SysprofPodman *self)
{
g_autofree char *path = NULL;
g_assert (self != NULL);
path = g_build_filename (g_get_user_data_dir (),
"containers",
"storage",
"overlay-images",
"images.json",
NULL);
json_parser_load_from_file (self->images_parser, path, NULL);
}
SysprofPodman *
sysprof_podman_snapshot_current_user (void)
{
SysprofPodman *self;
self = g_slice_new0 (SysprofPodman);
self->containers_parser = json_parser_new ();
self->layers_parser = json_parser_new ();
self->images_parser = json_parser_new ();
load_containers (self);
load_layers (self);
load_images (self);
return self;
}
static const char *
find_image_layer (JsonParser *parser,
const char *image)
{
JsonNode *root;
JsonArray *ar;
guint n_items;
g_assert (JSON_IS_PARSER (parser));
g_assert (image != NULL);
if (!(root = json_parser_get_root (parser)) ||
!JSON_NODE_HOLDS_ARRAY (root) ||
!(ar = json_node_get_array (root)))
return NULL;
n_items = json_array_get_length (ar);
for (guint i = 0; i < n_items; i++)
{
JsonObject *item = json_array_get_object_element (ar, i);
const char *id;
const char *layer;
if (item == NULL ||
!json_object_has_member (item, "id") ||
!json_object_has_member (item, "layer") ||
!(id = json_object_get_string_member (item, "id")) ||
strcmp (id, image) != 0 ||
!(layer = json_object_get_string_member (item, "layer")))
continue;
return layer;
}
return NULL;
}
static const char *
find_parent_layer (JsonParser *parser,
const char *layer,
GHashTable *seen)
{
JsonNode *root;
JsonArray *ar;
guint n_items;
g_assert (JSON_IS_PARSER (parser));
g_assert (layer != NULL);
g_assert (seen != NULL);
if (!(root = json_parser_get_root (parser)) ||
!JSON_NODE_HOLDS_ARRAY (root) ||
!(ar = json_node_get_array (root)))
return NULL;
n_items = json_array_get_length (ar);
for (guint i = 0; i < n_items; i++)
{
JsonObject *item = json_array_get_object_element (ar, i);
const char *parent;
const char *id;
if (item == NULL ||
!json_object_has_member (item, "id") ||
!json_object_has_member (item, "parent") ||
!(id = json_object_get_string_member (item, "id")) ||
strcmp (id, layer) != 0 ||
!(parent = json_object_get_string_member (item, "parent")))
continue;
if (g_hash_table_contains (seen, parent))
return NULL;
return parent;
}
return NULL;
}
static char *
get_layer_dir (const char *layer)
{
/* We don't use XDG data dir because this might be in a container
* or flatpak environment that doesn't match. And generally, it's
* always .local.
*/
return g_build_filename (g_get_home_dir (),
".local",
"share",
"containers",
"storage",
"overlay",
layer,
"diff",
NULL);
}
gchar **
sysprof_podman_get_layers (SysprofPodman *self,
const char *container)
{
const char *layer = NULL;
const char *image = NULL;
GHashTable *layers;
JsonNode *root;
JsonArray *ar;
const char **keys;
char **ret;
guint n_items;
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (container != NULL, NULL);
if (!(root = json_parser_get_root (self->containers_parser)) ||
!JSON_NODE_HOLDS_ARRAY (root) ||
!(ar = json_node_get_array (root)))
return NULL;
n_items = json_array_get_length (ar);
/* First try to locate the "layer" identifier for the container
* in question.
*/
for (guint i = 0; i < n_items; i++)
{
JsonObject *item = json_array_get_object_element (ar, i);
const char *item_layer;
const char *id;
if (item == NULL ||
!(id = json_object_get_string_member (item, "id")) ||
strcmp (id, container) != 0 ||
!(item_layer = json_object_get_string_member (item, "layer")))
continue;
layer = item_layer;
image = json_object_get_string_member (item, "image");
break;
}
layers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
/* Add all our known layers starting from current layer */
do
g_hash_table_add (layers, get_layer_dir (layer));
while ((layer = find_parent_layer (self->layers_parser, layer, layers)));
/* If an image was specified, add its layer */
if ((layer = find_image_layer (self->images_parser, image)))
{
do
g_hash_table_add (layers, get_layer_dir (layer));
while ((layer = find_parent_layer (self->layers_parser, layer, layers)));
}
keys = (const char **)g_hash_table_get_keys_as_array (layers, NULL);
ret = g_strdupv ((char **)keys);
g_hash_table_unref (layers);
g_free (keys);
return ret;
}

View File

@ -1,37 +0,0 @@
/* sysprof-podman.h
*
* 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
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
typedef struct _SysprofPodman SysprofPodman;
gchar **sysprof_podman_debug_dirs (void);
SysprofPodman *sysprof_podman_snapshot_current_user (void);
gchar **sysprof_podman_get_layers (SysprofPodman *self,
const char *container);
void sysprof_podman_free (SysprofPodman *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofPodman, sysprof_podman_free)
G_END_DECLS

View File

@ -1,39 +0,0 @@
/* sysprof-polkit-private.h
*
* Copyright 2019 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
*/
#pragma once
#include <gio/gio.h>
G_BEGIN_DECLS
G_GNUC_INTERNAL
void _sysprof_polkit_authorize_for_bus_async (GDBusConnection *bus,
const gchar *policy,
GHashTable *details,
gboolean allow_user_interaction,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
G_GNUC_INTERNAL
gboolean _sysprof_polkit_authorize_for_bus_finish (GAsyncResult *result,
GError **error);
G_END_DECLS

View File

@ -1,177 +0,0 @@
/* sysprof-polkit.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-polkit"
#include "config.h"
#if HAVE_POLKIT
# include <polkit/polkit.h>
#endif
#include "sysprof-polkit-private.h"
#include "sysprof-backport-autocleanups.h"
#if HAVE_POLKIT
typedef struct
{
const gchar *policy;
PolkitSubject *subject;
GHashTable *details;
guint allow_user_interaction : 1;
} Authorize;
static void
authorize_free (gpointer data)
{
Authorize *auth = data;
g_clear_object (&auth->subject);
g_clear_pointer (&auth->details, g_hash_table_unref);
g_slice_free (Authorize, auth);
}
static void
sysprof_polkit_check_authorization_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
PolkitAuthority *authority = (PolkitAuthority *)object;
g_autoptr(PolkitAuthorizationResult) res = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
g_assert (POLKIT_IS_AUTHORITY (authority));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (!(res = polkit_authority_check_authorization_finish (authority, result, &error)))
g_task_return_error (task, g_steal_pointer (&error));
else if (!polkit_authorization_result_get_is_authorized (res))
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_PROXY_AUTH_FAILED,
"Failed to authorize user credentials");
else
g_task_return_boolean (task, TRUE);
}
static void
sysprof_polkit_get_authority_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(PolkitAuthority) authority = NULL;
g_autoptr(PolkitDetails) details = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
GCancellable *cancellable;
Authorize *auth;
guint flags = 0;
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
cancellable = g_task_get_cancellable (task);
auth = g_task_get_task_data (task);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
g_assert (auth != NULL);
g_assert (POLKIT_IS_SUBJECT (auth->subject));
if (!(authority = polkit_authority_get_finish (result, &error)))
{
g_task_return_error (task, g_steal_pointer (&error));
return;
}
if (auth->allow_user_interaction)
flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
if (auth->details != NULL)
{
GHashTableIter iter;
gpointer k, v;
details = polkit_details_new ();
g_hash_table_iter_init (&iter, auth->details);
while (g_hash_table_iter_next (&iter, &k, &v))
polkit_details_insert (details, k, v);
}
polkit_authority_check_authorization (authority,
auth->subject,
auth->policy,
details,
flags,
cancellable,
sysprof_polkit_check_authorization_cb,
g_steal_pointer (&task));
}
#endif
void
_sysprof_polkit_authorize_for_bus_async (GDBusConnection *bus,
const gchar *policy,
GHashTable *details,
gboolean allow_user_interaction,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
#if HAVE_POLKIT
const gchar *bus_name;
Authorize *auth;
#endif
g_return_if_fail (G_IS_DBUS_CONNECTION (bus));
g_return_if_fail (policy != NULL);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, _sysprof_polkit_authorize_for_bus_async);
#if HAVE_POLKIT
bus_name = g_dbus_connection_get_unique_name (bus);
auth = g_slice_new0 (Authorize);
auth->subject = polkit_system_bus_name_new (bus_name);
auth->policy = g_intern_string (policy);
auth->details = details ? g_hash_table_ref (details) : NULL;
auth->allow_user_interaction = !!allow_user_interaction;
g_task_set_task_data (task, auth, authorize_free);
polkit_authority_get_async (cancellable,
sysprof_polkit_get_authority_cb,
g_steal_pointer (&task));
#else
g_task_return_boolean (task, TRUE);
#endif
}
gboolean
_sysprof_polkit_authorize_for_bus_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (G_IS_TASK (result), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}

View File

@ -1,153 +0,0 @@
/* sysprof-preload-source.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
*/
#define G_LOG_DOMAIN "sysprof-preload-source.h"
#include "config.h"
#include "sysprof-preload-source.h"
struct _SysprofPreloadSource
{
GObject parent_instance;
gchar *preload;
};
enum {
PROP_0,
PROP_PRELOAD,
N_PROPS
};
static void
sysprof_preload_source_modify_spawn (SysprofSource *source,
SysprofSpawnable *spawnable)
{
SysprofPreloadSource *self = (SysprofPreloadSource *)source;
g_assert (SYSPROF_IS_SOURCE (self));
g_assert (SYSPROF_IS_SPAWNABLE (spawnable));
#ifdef __linux__
if (self->preload)
{
g_autofree gchar *freeme = NULL;
const gchar *ld_preload;
if (!(ld_preload = sysprof_spawnable_getenv (spawnable, "LD_PRELOAD")))
sysprof_spawnable_setenv (spawnable, "LD_PRELOAD", self->preload);
else
sysprof_spawnable_setenv (spawnable,
"LD_PRELOAD",
(freeme = g_strdup_printf ("%s:%s", self->preload, ld_preload)));
}
#endif
}
static void
sysprof_preload_source_stop (SysprofSource *source)
{
sysprof_source_emit_finished (source);
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->modify_spawn = sysprof_preload_source_modify_spawn;
iface->stop = sysprof_preload_source_stop;
}
G_DEFINE_TYPE_WITH_CODE (SysprofPreloadSource, sysprof_preload_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static GParamSpec *properties [N_PROPS];
static void
sysprof_preload_source_finalize (GObject *object)
{
SysprofPreloadSource *self = (SysprofPreloadSource *)object;
g_clear_pointer (&self->preload, g_free);
G_OBJECT_CLASS (sysprof_preload_source_parent_class)->finalize (object);
}
static void
sysprof_preload_source_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofPreloadSource *self = SYSPROF_PRELOAD_SOURCE (object);
switch (prop_id)
{
case PROP_PRELOAD:
g_value_set_string (value, self->preload);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_preload_source_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofPreloadSource *self = SYSPROF_PRELOAD_SOURCE (object);
switch (prop_id)
{
case PROP_PRELOAD:
g_free (self->preload);
self->preload = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_preload_source_class_init (SysprofPreloadSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_preload_source_finalize;
object_class->get_property = sysprof_preload_source_get_property;
object_class->set_property = sysprof_preload_source_set_property;
properties [PROP_PRELOAD] =
g_param_spec_string ("preload",
"Preload",
"The preload to load into the process",
NULL,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_preload_source_init (SysprofPreloadSource *self)
{
}

View File

@ -1,32 +0,0 @@
/* sysprof-preload-source.h
*
* 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
*/
#pragma once
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_PRELOAD_SOURCE (sysprof_preload_source_get_type())
SYSPROF_AVAILABLE_IN_3_38
G_DECLARE_FINAL_TYPE (SysprofPreloadSource, sysprof_preload_source, SYSPROF, PRELOAD_SOURCE, GObject)
G_END_DECLS

View File

@ -1,39 +0,0 @@
/* sysprof-private.h
*
* Copyright 2019 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
*/
#pragma once
#include <sysprof-capture.h>
#include "sysprof.h"
#include "sysprof-kallsyms.h"
G_BEGIN_DECLS
typedef GArray SysprofKernelSymbols;
SysprofKernelSymbols *_sysprof_kernel_symbols_get_shared (void);
SysprofKernelSymbols *_sysprof_kernel_symbols_new_from_kallsyms (SysprofKallsyms *kallsyms);
const SysprofKernelSymbol *_sysprof_kernel_symbols_lookup (const SysprofKernelSymbols *self,
SysprofCaptureAddress address);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofKernelSymbols, g_array_unref)
G_END_DECLS

View File

@ -1,613 +0,0 @@
/* sysprof-proc-source.c
*
* Copyright 2016-2019 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
*/
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* Copyright 2004, 2005, Soeren Sandmann
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <json-glib/json-glib.h>
#include "sysprof-helpers.h"
#include "sysprof-podman.h"
#include "sysprof-proc-source.h"
struct _SysprofProcSource
{
GObject parent_instance;
SysprofCaptureWriter *writer;
GArray *pids;
SysprofPodman *podman;
guint is_ready : 1;
};
static void source_iface_init (SysprofSourceInterface *iface);
G_DEFINE_TYPE_EXTENDED (SysprofProcSource, sysprof_proc_source, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static void
sysprof_proc_source_populate_process (SysprofProcSource *self,
GPid pid,
const gchar *cmdline)
{
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (pid > 0);
sysprof_capture_writer_add_process (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
pid,
cmdline);
}
static void
sysprof_proc_source_populate_maps (SysprofProcSource *self,
GPid pid,
const gchar *mapsstr,
gboolean ignore_inode)
{
g_auto(GStrv) lines = NULL;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (mapsstr != NULL);
g_assert (pid > 0);
lines = g_strsplit (mapsstr, "\n", 0);
for (guint i = 0; lines[i] != NULL; i++)
{
gchar file[512];
gulong start;
gulong end;
gulong offset;
gulong inode;
gint r;
r = sscanf (lines[i],
"%lx-%lx %*15s %lx %*x:%*x %lu %511[^\n]",
&start, &end, &offset, &inode, file);
file [sizeof file - 1] = '\0';
/* file has a " (deleted)" suffix if it was deleted from disk */
if (g_str_has_suffix (file, " (deleted)"))
file [strlen (file) - strlen (" (deleted)")] = '\0';
if (r != 5)
continue;
if (ignore_inode || strcmp ("[vdso]", file) == 0)
{
/*
* Søren Sandmann Pedersen says:
*
* For the vdso, the kernel reports 'offset' as the
* the same as the mapping addres. This doesn't make
* any sense to me, so we just zero it here. There
* is code in binfile.c (read_inode) that returns 0
* for [vdso].
*/
offset = 0;
inode = 0;
}
sysprof_capture_writer_add_map (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
pid,
start,
end,
offset,
inode,
file);
}
}
static gboolean
pid_is_interesting (SysprofProcSource *self,
GPid pid)
{
if (self->pids == NULL || self->pids->len == 0)
return TRUE;
for (guint i = 0; i < self->pids->len; i++)
{
if (g_array_index (self->pids, GPid, i) == pid)
return TRUE;
}
return FALSE;
}
static void
add_file (SysprofProcSource *self,
int pid,
const char *path,
const char *data)
{
gsize to_write = strlen (data);
while (to_write > 0)
{
gsize this_write = MIN (to_write, 4096 * 2);
to_write -= this_write;
sysprof_capture_writer_add_file (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
pid,
path,
to_write == 0,
(const guint8 *)data,
this_write);
data += this_write;
}
}
static void
sysprof_proc_source_populate_mountinfo (SysprofProcSource *self,
GPid pid,
const char *mountinfo)
{
g_autofree char *path = NULL;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (self->writer != NULL);
g_assert (mountinfo != NULL);
path = g_strdup_printf ("/proc/%d/mountinfo", pid);
add_file (self, pid, path, mountinfo);
}
static void
sysprof_proc_source_populate_pid_podman (SysprofProcSource *self,
GPid pid,
const char *container)
{
g_auto(GStrv) layers = NULL;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (container != NULL);
if (!(layers = sysprof_podman_get_layers (self->podman, container)))
return;
for (guint i = 0; layers[i]; i++)
sysprof_capture_writer_add_overlay (self->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1, pid, i, layers[i], "/");
}
static void
sysprof_proc_source_populate_overlays (SysprofProcSource *self,
GPid pid,
const char *cgroup)
{
static GRegex *flatpak;
static GRegex *podman;
g_autoptr(GMatchInfo) flatpak_match = NULL;
g_autoptr(GMatchInfo) podman_match = NULL;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (cgroup != NULL);
if (strcmp (cgroup, "") == 0)
return;
/* This function tries to discover the podman container that contains the
* process identified on the host as @pid. We can only do anything with this
* if the pids are in containers that the running user controls (so that we
* can actually access the overlays).
*
* This stuff, and I want to emphasize, is a giant hack. Just like containers
* are on Linux. But if we are really careful, we can make this work for the
* one particular use case I care about, which is podman/toolbox on Fedora
* Workstation/Silverblue.
*
* -- Christian
*/
if G_UNLIKELY (podman == NULL)
{
podman = g_regex_new ("libpod-([a-z0-9]{64})\\.scope", G_REGEX_OPTIMIZE, 0, NULL);
g_assert (podman != NULL);
}
if (flatpak == NULL)
{
flatpak = g_regex_new ("app-flatpak-([a-zA-Z_\\-\\.]+)-[0-9]+\\.scope", G_REGEX_OPTIMIZE, 0, NULL);
g_assert (flatpak != NULL);
}
if (g_regex_match (podman, cgroup, 0, &podman_match))
{
SysprofHelpers *helpers = sysprof_helpers_get_default ();
g_autofree char *word = g_match_info_fetch (podman_match, 1);
g_autofree char *path = g_strdup_printf ("/proc/%d/root/run/.containerenv", pid);
g_autofree char *info = NULL;
sysprof_proc_source_populate_pid_podman (self, pid, word);
if (sysprof_helpers_get_proc_file (helpers, path, NULL, &info, NULL))
add_file (self, pid, "/run/.containerenv", info);
}
else if (g_regex_match (flatpak, cgroup, 0, &flatpak_match))
{
SysprofHelpers *helpers = sysprof_helpers_get_default ();
g_autofree char *word = g_match_info_fetch (flatpak_match, 1);
g_autofree char *path = g_strdup_printf ("/proc/%d/root/.flatpak-info", pid);
g_autofree char *info = NULL;
if (sysprof_helpers_get_proc_file (helpers, path, NULL, &info, NULL))
add_file (self, pid, "/.flatpak-info", info);
}
}
static void
sysprof_proc_source_populate (SysprofProcSource *self,
GVariant *info)
{
g_autofree gchar *mounts = NULL;
SysprofHelpers *helpers;
gsize n_pids = 0;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (info != NULL);
g_assert (g_variant_is_of_type (info, G_VARIANT_TYPE ("aa{sv}")));
if (self->writer == NULL)
return;
if (self->podman == NULL)
self->podman = sysprof_podman_snapshot_current_user ();
helpers = sysprof_helpers_get_default ();
if (!sysprof_helpers_get_proc_file (helpers, "/proc/mounts", NULL, &mounts, NULL))
return;
add_file (self, -1, "/proc/mounts", mounts);
n_pids = g_variant_n_children (info);
for (gsize i = 0; i < n_pids; i++)
{
g_autoptr(GVariant) pidinfo = g_variant_get_child_value (info, i);
GVariantDict dict;
const gchar *cmdline;
const gchar *comm;
const gchar *mountinfo;
const gchar *maps;
const gchar *cgroup;
gboolean ignore_inode;
gint32 pid;
g_variant_dict_init (&dict, pidinfo);
if (!g_variant_dict_lookup (&dict, "pid", "i", &pid) ||
!pid_is_interesting (self, pid))
goto skip;
if (!g_variant_dict_lookup (&dict, "cmdline", "&s", &cmdline))
cmdline = "";
if (!g_variant_dict_lookup (&dict, "comm", "&s", &comm))
comm = "";
if (!g_variant_dict_lookup (&dict, "mountinfo", "&s", &mountinfo))
mountinfo = "";
if (!g_variant_dict_lookup (&dict, "maps", "&s", &maps))
maps = "";
if (!g_variant_dict_lookup (&dict, "cgroup", "&s", &cgroup))
cgroup = "";
/* Ignore inodes from podman/toolbox because they appear
* to always be wrong. We'll have to rely on CRC instead.
*/
ignore_inode = strstr (cgroup, "/libpod-") != NULL;
sysprof_proc_source_populate_process (self, pid, *cmdline ? cmdline : comm);
sysprof_proc_source_populate_mountinfo (self, pid, mountinfo);
sysprof_proc_source_populate_maps (self, pid, maps, ignore_inode);
sysprof_proc_source_populate_overlays (self, pid, cgroup);
skip:
g_variant_dict_clear (&dict);
}
}
static void
sysprof_proc_source_get_process_info_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofHelpers *helpers = (SysprofHelpers *)object;
g_autoptr(SysprofProcSource) self = user_data;
g_autoptr(GVariant) info = NULL;
g_autoptr(GError) error = NULL;
g_assert (SYSPROF_IS_HELPERS (helpers));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_PROC_SOURCE (self));
if (!sysprof_helpers_get_process_info_finish (helpers, result, &info, &error))
{
sysprof_source_emit_failed (SYSPROF_SOURCE (self), error);
return;
}
sysprof_proc_source_populate (self, info);
sysprof_source_emit_finished (SYSPROF_SOURCE (self));
}
typedef struct _StreamToCapture
{
SysprofCaptureWriter *writer;
GInputStream *stream;
guint8 *buf;
gsize buflen;
} StreamToCapture;
static void
stream_to_capture_free (StreamToCapture *state)
{
g_clear_pointer (&state->writer, sysprof_capture_writer_unref);
g_clear_pointer (&state->buf, g_free);
g_clear_object (&state->stream);
g_free (state);
}
static gboolean
stream_to_capture (StreamToCapture *state)
{
gssize n_read;
g_assert (state != NULL);
g_assert (state->writer != NULL);
g_assert (state->buf != NULL);
g_assert (state->buflen > 0);
g_assert (G_IS_INPUT_STREAM (state->stream));
n_read = g_input_stream_read (state->stream, state->buf, state->buflen, NULL, NULL);
if (n_read >= 0)
sysprof_capture_writer_add_file (state->writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
"/proc/kallsyms.gz",
n_read == 0,
state->buf,
n_read);
return n_read > 0;
}
static void
get_kallsyms_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofHelpers *helpers = (SysprofHelpers *)object;
g_autoptr(SysprofProcSource) self = user_data;
g_autoptr(GZlibCompressor) compressor = NULL;
g_autoptr(GInputStream) base_stream = NULL;
g_autoptr(GInputStream) gz_stream = NULL;
g_autoptr(GError) error = NULL;
g_autofree char *contents = NULL;
StreamToCapture *state;
g_assert (SYSPROF_IS_HELPERS (helpers));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_PROC_SOURCE (self));
if (!sysprof_helpers_get_proc_file_finish (helpers, result, &contents, &error))
return;
base_stream = g_memory_input_stream_new_from_data (g_steal_pointer (&contents), -1, g_free);
compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, 6);
gz_stream = g_converter_input_stream_new (base_stream, G_CONVERTER (compressor));
state = g_new0 (StreamToCapture, 1);
state->stream = g_object_ref (gz_stream);
state->writer = sysprof_capture_writer_ref (self->writer);
state->buflen = 16100; /* 16kb write buffer - framing overhead */
state->buf = g_malloc (state->buflen);
g_idle_add_full (G_PRIORITY_HIGH,
(GSourceFunc)stream_to_capture,
state,
(GDestroyNotify)stream_to_capture_free);
}
static void
sysprof_proc_source_start (SysprofSource *source)
{
SysprofProcSource *self = (SysprofProcSource *)source;
SysprofHelpers *helpers = sysprof_helpers_get_default ();
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (self->writer != NULL);
sysprof_helpers_get_process_info_async (helpers,
"pid,maps,mountinfo,cmdline,comm,cgroup",
NULL,
sysprof_proc_source_get_process_info_cb,
g_object_ref (self));
/* We must read kallsyms from sysprofd because if we ask for a FD and read
* it back from our current process, it's likely the kernel will censor our
* addresses making them useless.
*
* That means a pretty large (couple MB transfer) over D-Bus which is not
* great but also not the end of the world so long as we do it async.
*/
sysprof_helpers_get_proc_file_async (helpers,
"/proc/kallsyms",
NULL,
get_kallsyms_cb,
g_object_ref (self));
}
static void
sysprof_proc_source_stop (SysprofSource *source)
{
SysprofProcSource *self = (SysprofProcSource *)source;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
}
static void
sysprof_proc_source_set_writer (SysprofSource *source,
SysprofCaptureWriter *writer)
{
SysprofProcSource *self = (SysprofProcSource *)source;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (writer != NULL);
self->writer = sysprof_capture_writer_ref (writer);
}
static void
sysprof_proc_source_add_pid (SysprofSource *source,
GPid pid)
{
SysprofProcSource *self = (SysprofProcSource *)source;
guint i;
g_assert (SYSPROF_IS_PROC_SOURCE (self));
g_assert (pid > -1);
for (i = 0; i < self->pids->len; i++)
{
GPid ele = g_array_index (self->pids, GPid, i);
if (ele == pid)
return;
}
g_array_append_val (self->pids, pid);
}
static void
sysprof_proc_source_auth_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofHelpers *helpers = (SysprofHelpers *)object;
g_autoptr(SysprofProcSource) self = user_data;
g_autoptr(GError) error = NULL;
g_assert (SYSPROF_IS_HELPERS (helpers));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_PROC_SOURCE (self));
if (!sysprof_helpers_authorize_finish (helpers, result, &error))
{
sysprof_source_emit_failed (SYSPROF_SOURCE (self), error);
}
else
{
self->is_ready = TRUE;
sysprof_source_emit_ready (SYSPROF_SOURCE (self));
}
}
static void
sysprof_proc_source_prepare (SysprofSource *source)
{
g_assert (SYSPROF_IS_PROC_SOURCE (source));
sysprof_helpers_authorize_async (sysprof_helpers_get_default (),
NULL,
sysprof_proc_source_auth_cb,
g_object_ref (source));
}
static gboolean
sysprof_proc_source_get_is_ready (SysprofSource *source)
{
return SYSPROF_PROC_SOURCE (source)->is_ready;
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->set_writer = sysprof_proc_source_set_writer;
iface->start = sysprof_proc_source_start;
iface->stop = sysprof_proc_source_stop;
iface->add_pid = sysprof_proc_source_add_pid;
iface->prepare = sysprof_proc_source_prepare;
iface->get_is_ready = sysprof_proc_source_get_is_ready;
}
static void
sysprof_proc_source_finalize (GObject *object)
{
SysprofProcSource *self = (SysprofProcSource *)object;
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
g_clear_pointer (&self->pids, g_array_unref);
g_clear_pointer (&self->podman, sysprof_podman_free);
G_OBJECT_CLASS (sysprof_proc_source_parent_class)->finalize (object);
}
static void
sysprof_proc_source_class_init (SysprofProcSourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_proc_source_finalize;
}
static void
sysprof_proc_source_init (SysprofProcSource *self)
{
self->pids = g_array_new (FALSE, FALSE, sizeof (GPid));
}
SysprofSource *
sysprof_proc_source_new (void)
{
return g_object_new (SYSPROF_TYPE_PROC_SOURCE, NULL);
}

View File

@ -1,41 +0,0 @@
/* sysprof-proc-source.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-version-macros.h"
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_PROC_SOURCE (sysprof_proc_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofProcSource, sysprof_proc_source, SYSPROF, PROC_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_proc_source_new (void);
G_END_DECLS

View File

@ -1,255 +0,0 @@
/* sysprof-process-model-item.c
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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
*/
#define G_LOG_DOMAIN "sp-process-model-item"
#include "config.h"
#include <string.h>
#include "sysprof-process-model-item.h"
#ifdef __linux__
# include "sysprof-proc-source.h"
#endif
struct _SysprofProcessModelItem
{
GObject parent_instance;
GPid pid;
gchar *command_line; /* Short version (first field) */
gchar **argv; /* Long version (argv as a strv) */
guint is_kernel : 1;
};
G_DEFINE_TYPE (SysprofProcessModelItem, sysprof_process_model_item, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_COMMAND_LINE,
PROP_PID,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static void
sysprof_process_model_item_finalize (GObject *object)
{
SysprofProcessModelItem *self = (SysprofProcessModelItem *)object;
g_clear_pointer (&self->command_line, g_free);
g_clear_pointer (&self->argv, g_strfreev);
G_OBJECT_CLASS (sysprof_process_model_item_parent_class)->finalize (object);
}
static void
sysprof_process_model_item_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofProcessModelItem *self = SYSPROF_PROCESS_MODEL_ITEM (object);
switch (prop_id)
{
case PROP_COMMAND_LINE:
g_value_set_string (value, self->command_line);
break;
case PROP_PID:
g_value_set_int (value, self->pid);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_process_model_item_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofProcessModelItem *self = SYSPROF_PROCESS_MODEL_ITEM (object);
switch (prop_id)
{
case PROP_COMMAND_LINE:
self->command_line = g_value_dup_string (value);
break;
case PROP_PID:
self->pid = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_process_model_item_class_init (SysprofProcessModelItemClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_process_model_item_finalize;
object_class->get_property = sysprof_process_model_item_get_property;
object_class->set_property = sysprof_process_model_item_set_property;
properties [PROP_COMMAND_LINE] =
g_param_spec_string ("command-line",
"Command Line",
"Command Line",
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
properties [PROP_PID] =
g_param_spec_int ("pid",
"Pid",
"Pid",
-1,
G_MAXINT,
-1,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_process_model_item_init (SysprofProcessModelItem *self)
{
}
SysprofProcessModelItem *
sysprof_process_model_item_new_from_variant (GVariant *info)
{
SysprofProcessModelItem *ret;
GVariantDict dict;
const gchar *cmdline;
g_return_val_if_fail (info != NULL, NULL);
g_return_val_if_fail (g_variant_is_of_type (info, G_VARIANT_TYPE_VARDICT), NULL);
ret = g_object_new (SYSPROF_TYPE_PROCESS_MODEL_ITEM, NULL);
g_variant_dict_init (&dict, info);
if (g_variant_dict_lookup (&dict, "cmdline", "&s", &cmdline) && *cmdline)
{
if (g_shell_parse_argv (cmdline, NULL, &ret->argv, NULL))
ret->command_line = g_strdup (ret->argv[0]);
}
else if (g_variant_dict_lookup (&dict, "comm", "&s", &cmdline))
{
ret->argv = g_new0 (gchar *, 2);
ret->argv[0] = g_strdup (cmdline);
ret->is_kernel = TRUE;
}
g_variant_dict_lookup (&dict, "pid", "i", &ret->pid);
g_variant_dict_clear (&dict);
return g_steal_pointer (&ret);
}
guint
sysprof_process_model_item_hash (SysprofProcessModelItem *self)
{
g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), 0);
return self->pid;
}
gboolean
sysprof_process_model_item_equal (SysprofProcessModelItem *self,
SysprofProcessModelItem *other)
{
g_assert (SYSPROF_IS_PROCESS_MODEL_ITEM (self));
g_assert (SYSPROF_IS_PROCESS_MODEL_ITEM (other));
return ((self->pid == other->pid) &&
(g_strcmp0 (self->command_line, other->command_line) == 0));
}
GPid
sysprof_process_model_item_get_pid (SysprofProcessModelItem *self)
{
g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), 0);
return self->pid;
}
const gchar *
sysprof_process_model_item_get_command_line (SysprofProcessModelItem *self)
{
g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), NULL);
return self->command_line;
}
gboolean
sysprof_process_model_item_is_kernel (SysprofProcessModelItem *self)
{
g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), FALSE);
return self->is_kernel;
}
const gchar * const *
sysprof_process_model_item_get_argv (SysprofProcessModelItem *self)
{
g_autofree gchar *contents = NULL;
g_autofree gchar *path = NULL;
const gchar *pos;
const gchar *endptr;
GPtrArray *ar;
gsize size = 0;
GPid pid;
g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), NULL);
if (self->argv)
return (const gchar * const *)self->argv;
if ((pid = sysprof_process_model_item_get_pid (self)) < 0)
return NULL;
path = g_strdup_printf ("/proc/%u/cmdline", (guint)pid);
if (!g_file_get_contents (path, &contents, &size, NULL))
return NULL;
ar = g_ptr_array_new ();
/* Each parameter is followed by \0 */
for (pos = contents, endptr = contents + size;
pos < endptr;
pos += strlen (pos) + 1)
g_ptr_array_add (ar, g_strdup (pos));
g_ptr_array_add (ar, NULL);
g_clear_pointer (&self->argv, g_strfreev);
self->argv = (gchar **)g_ptr_array_free (ar, FALSE);
return (const gchar * const *)self->argv;
}

View File

@ -1,54 +0,0 @@
/* sysprof-process-model-item.h
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-version-macros.h"
#include <gio/gio.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_PROCESS_MODEL_ITEM (sysprof_process_model_item_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofProcessModelItem, sysprof_process_model_item, SYSPROF, PROCESS_MODEL_ITEM, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofProcessModelItem *sysprof_process_model_item_new_from_variant (GVariant *info);
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_process_model_item_hash (SysprofProcessModelItem *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_process_model_item_equal (SysprofProcessModelItem *self,
SysprofProcessModelItem *other);
SYSPROF_AVAILABLE_IN_ALL
GPid sysprof_process_model_item_get_pid (SysprofProcessModelItem *self);
SYSPROF_AVAILABLE_IN_ALL
const gchar *sysprof_process_model_item_get_command_line (SysprofProcessModelItem *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_process_model_item_is_kernel (SysprofProcessModelItem *self);
SYSPROF_AVAILABLE_IN_ALL
const gchar * const *sysprof_process_model_item_get_argv (SysprofProcessModelItem *self);
G_END_DECLS

View File

@ -1,307 +0,0 @@
/* sysprof-process-model.c
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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 <stdlib.h>
#include "sysprof-backport-autocleanups.h"
#include "sysprof-helpers.h"
#include "sysprof-process-model.h"
#include "sysprof-process-model-item.h"
#define QUEUE_RELOAD_TIMEOUT_MSEC 100
struct _SysprofProcessModel
{
GObject parent_instance;
GPtrArray *items;
guint reload_source;
guint no_proxy : 1;
};
static void list_model_iface_init (GListModelInterface *iface);
G_DEFINE_TYPE_EXTENDED (SysprofProcessModel, sysprof_process_model, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static void
sysprof_process_model_finalize (GObject *object)
{
SysprofProcessModel *self = (SysprofProcessModel *)object;
g_clear_handle_id (&self->reload_source, g_source_remove);
g_clear_pointer (&self->items, g_ptr_array_unref);
G_OBJECT_CLASS (sysprof_process_model_parent_class)->finalize (object);
}
static void
sysprof_process_model_class_init (SysprofProcessModelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_process_model_finalize;
}
static void
sysprof_process_model_init (SysprofProcessModel *self)
{
self->items = g_ptr_array_new_with_free_func (g_object_unref);
}
static guint
find_index (GPtrArray *ar,
GPid pid)
{
guint i;
g_assert (ar != NULL);
for (i = 0; i < ar->len; i++)
{
SysprofProcessModelItem *item = g_ptr_array_index (ar, i);
GPid item_pid = sysprof_process_model_item_get_pid (item);
g_assert (pid != item_pid);
if (item_pid > pid)
return i;
}
return ar->len;
}
static void
sysprof_process_model_merge_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofProcessModel *self = (SysprofProcessModel *)object;
g_autoptr(GPtrArray) ret = NULL;
g_autoptr(GHashTable) old_hash = NULL;
g_autoptr(GHashTable) new_hash = NULL;
GError *error = NULL;
guint i;
g_assert (SYSPROF_IS_PROCESS_MODEL (self));
g_assert (G_IS_TASK (result));
ret = g_task_propagate_pointer (G_TASK (result), &error);
if (ret == NULL)
{
g_warning ("%s", error->message);
g_clear_error (&error);
return;
}
/*
* TODO: Clearly this could be optimized to walk both arrays at once
* and do a proper 2-way merge.
*/
old_hash = g_hash_table_new ((GHashFunc)sysprof_process_model_item_hash,
(GEqualFunc)sysprof_process_model_item_equal);
new_hash = g_hash_table_new ((GHashFunc)sysprof_process_model_item_hash,
(GEqualFunc)sysprof_process_model_item_equal);
for (i = 0; i < self->items->len; i++)
{
SysprofProcessModelItem *item = g_ptr_array_index (self->items, i);
g_hash_table_insert (old_hash, item, NULL);
}
for (i = 0; i < ret->len; i++)
{
SysprofProcessModelItem *item = g_ptr_array_index (ret, i);
g_hash_table_insert (new_hash, item, NULL);
}
for (i = self->items->len; i > 0; i--)
{
guint index = i - 1;
SysprofProcessModelItem *item = g_ptr_array_index (self->items, index);
if (!g_hash_table_contains (new_hash, item))
{
g_ptr_array_remove_index (self->items, index);
g_list_model_items_changed (G_LIST_MODEL (self), index, 1, 0);
}
}
for (i = 0; i < ret->len; i++)
{
SysprofProcessModelItem *item = g_ptr_array_index (ret, i);
GPid pid;
guint index;
if (g_hash_table_contains (old_hash, item))
continue;
pid = sysprof_process_model_item_get_pid (item);
index = find_index (self->items, pid);
g_ptr_array_insert (self->items, index, g_object_ref (item));
g_list_model_items_changed (G_LIST_MODEL (self), index, 0, 1);
}
}
static gint
compare_by_pid (gconstpointer a,
gconstpointer b)
{
SysprofProcessModelItem **aitem = (SysprofProcessModelItem **)a;
SysprofProcessModelItem **bitem = (SysprofProcessModelItem **)b;
return sysprof_process_model_item_get_pid (*aitem) - sysprof_process_model_item_get_pid (*bitem);
}
static void
sysprof_process_model_reload_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SysprofProcessModel *self = source_object;
SysprofHelpers *helpers = sysprof_helpers_get_default ();
g_autoptr(GPtrArray) ret = NULL;
g_autoptr(GVariant) info = NULL;
g_assert (SYSPROF_IS_PROCESS_MODEL (source_object));
g_assert (G_IS_TASK (task));
ret = g_ptr_array_new_with_free_func (g_object_unref);
if (sysprof_helpers_get_process_info (helpers, "pid,cmdline,comm", self->no_proxy, NULL, &info, NULL))
{
gsize n_children = g_variant_n_children (info);
for (gsize i = 0; i < n_children; i++)
{
g_autoptr(GVariant) pidinfo = g_variant_get_child_value (info, i);
g_autoptr(SysprofProcessModelItem) item = sysprof_process_model_item_new_from_variant (pidinfo);
if (sysprof_process_model_item_is_kernel (item))
continue;
g_ptr_array_add (ret, g_steal_pointer (&item));
}
g_ptr_array_sort (ret, compare_by_pid);
}
g_task_return_pointer (task,
g_steal_pointer (&ret),
(GDestroyNotify)g_ptr_array_unref);
}
static gboolean
sysprof_process_model_do_reload (gpointer user_data)
{
SysprofProcessModel *self = user_data;
g_autoptr(GTask) task = NULL;
g_clear_handle_id (&self->reload_source, g_source_remove);
task = g_task_new (self, NULL, sysprof_process_model_merge_cb, NULL);
g_task_set_priority (task, G_PRIORITY_LOW);
g_task_run_in_thread (task, sysprof_process_model_reload_worker);
return G_SOURCE_REMOVE;
}
SysprofProcessModel *
sysprof_process_model_new (void)
{
return g_object_new (SYSPROF_TYPE_PROCESS_MODEL, NULL);
}
void
sysprof_process_model_reload (SysprofProcessModel *self)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SYSPROF_IS_PROCESS_MODEL (self));
g_clear_handle_id (&self->reload_source, g_source_remove);
task = g_task_new (self, NULL, NULL, NULL);
g_task_set_priority (task, G_PRIORITY_LOW);
g_task_run_in_thread_sync (task, sysprof_process_model_reload_worker);
sysprof_process_model_merge_cb (G_OBJECT (self), G_ASYNC_RESULT (task), NULL);
}
void
sysprof_process_model_queue_reload (SysprofProcessModel *self)
{
g_return_if_fail (SYSPROF_IS_PROCESS_MODEL (self));
if (self->reload_source == 0)
self->reload_source = g_timeout_add (QUEUE_RELOAD_TIMEOUT_MSEC,
sysprof_process_model_do_reload,
self);
}
static GType
sysprof_process_model_get_item_type (GListModel *model)
{
return SYSPROF_TYPE_PROCESS_MODEL_ITEM;
}
static guint
sysprof_process_model_get_n_items (GListModel *model)
{
SysprofProcessModel *self = (SysprofProcessModel *)model;
return self->items->len;
}
static gpointer
sysprof_process_model_get_item (GListModel *model,
guint position)
{
SysprofProcessModel *self = (SysprofProcessModel *)model;
g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL (self), NULL);
g_return_val_if_fail (position < self->items->len, NULL);
return g_object_ref (g_ptr_array_index (self->items, position));
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = sysprof_process_model_get_item_type;
iface->get_n_items = sysprof_process_model_get_n_items;
iface->get_item = sysprof_process_model_get_item;
}
void
sysprof_process_model_set_no_proxy (SysprofProcessModel *self,
gboolean no_proxy)
{
g_return_if_fail (SYSPROF_IS_PROCESS_MODEL (self));
self->no_proxy = !!no_proxy;
}

View File

@ -1,48 +0,0 @@
/* sysprof-process-model.h
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include <gio/gio.h>
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_PROCESS_MODEL (sysprof_process_model_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofProcessModel, sysprof_process_model, SYSPROF, PROCESS_MODEL, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofProcessModel *sysprof_process_model_new (void);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_process_model_set_no_proxy (SysprofProcessModel *self,
gboolean no_proxy);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_process_model_reload (SysprofProcessModel *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_process_model_queue_reload (SysprofProcessModel *self);
G_END_DECLS

View File

@ -1,85 +0,0 @@
/* sysprof-profile.c
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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 "sysprof-profile.h"
G_DEFINE_INTERFACE (SysprofProfile, sysprof_profile, G_TYPE_OBJECT)
static void
dummy_generate (SysprofProfile *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
task = g_task_new (self, cancellable, callback, user_data);
g_task_return_boolean (task, TRUE);
}
static gboolean
dummy_generate_finish (SysprofProfile *self,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_boolean (G_TASK (result), error);
}
static void
sysprof_profile_default_init (SysprofProfileInterface *iface)
{
iface->generate = dummy_generate;
iface->generate_finish = dummy_generate_finish;
}
void
sysprof_profile_generate (SysprofProfile *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (SYSPROF_IS_PROFILE (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
SYSPROF_PROFILE_GET_IFACE (self)->generate (self, cancellable, callback, user_data);
}
gboolean
sysprof_profile_generate_finish (SysprofProfile *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (SYSPROF_IS_PROFILE (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
return SYSPROF_PROFILE_GET_IFACE (self)->generate_finish (self, result, error);
}
void
sysprof_profile_set_reader (SysprofProfile *self,
SysprofCaptureReader *reader)
{
g_return_if_fail (SYSPROF_IS_PROFILE (self));
g_return_if_fail (reader != NULL);
SYSPROF_PROFILE_GET_IFACE (self)->set_reader (self, reader);
}

View File

@ -1,67 +0,0 @@
/* sysprof-profile.h
*
* Copyright 2016-2019 Christian Hergert <christian@hergert.me>
*
* 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include <gio/gio.h>
#include "sysprof-capture-reader.h"
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_PROFILE (sysprof_profile_get_type ())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (SysprofProfile, sysprof_profile, SYSPROF, PROFILE, GObject)
struct _SysprofProfileInterface
{
GTypeInterface parent;
void (*set_reader) (SysprofProfile *self,
SysprofCaptureReader *reader);
void (*generate) (SysprofProfile *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (*generate_finish) (SysprofProfile *self,
GAsyncResult *result,
GError **error);
};
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profile_set_reader (SysprofProfile *self,
SysprofCaptureReader *reader);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profile_generate (SysprofProfile *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_profile_generate_finish (SysprofProfile *self,
GAsyncResult *result,
GError **error);
G_END_DECLS

View File

@ -1,312 +0,0 @@
/* sysprof-profiler.c
*
* Copyright 2016-2019 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
*/
#define G_LOG_DOMAIN "sysprof-profiler"
#include "config.h"
#include "sysprof-profiler.h"
G_DEFINE_INTERFACE (SysprofProfiler, sysprof_profiler, G_TYPE_OBJECT)
enum {
FAILED,
STOPPED,
N_SIGNALS
};
static guint signals [N_SIGNALS];
static void
sysprof_profiler_default_init (SysprofProfilerInterface *iface)
{
signals [FAILED] = g_signal_new ("failed",
G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SysprofProfilerInterface, failed),
NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_ERROR);
signals [STOPPED] = g_signal_new ("stopped",
G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SysprofProfilerInterface, stopped),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
g_object_interface_install_property (iface,
g_param_spec_double ("elapsed",
"Elapsed",
"The amount of elapsed time profiling",
0,
G_MAXDOUBLE,
0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
g_object_interface_install_property (iface,
g_param_spec_boolean ("is-running",
"Is Running",
"If the profiler is currently running.",
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
g_object_interface_install_property (iface,
g_param_spec_boolean ("is-mutable",
"Is Mutable",
"If the profiler can still be prepared.",
TRUE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
g_object_interface_install_property (iface,
g_param_spec_boolean ("spawn-inherit-environ",
"Sysprofawn Inherit Environ",
"If the spawned child should inherit the parents environment",
TRUE,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_interface_install_property (iface,
g_param_spec_boolean ("whole-system",
"Whole System",
"If the whole system should be profiled",
TRUE,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_interface_install_property (iface,
g_param_spec_boolean ("spawn",
"Sysprofawn",
"If configured child should be spawned",
TRUE,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_interface_install_property (iface,
g_param_spec_boxed ("spawn-argv",
"Sysprofawn Argv",
"The arguments for the spawn child",
G_TYPE_STRV,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_interface_install_property (iface,
g_param_spec_string ("spawn-cwd",
"Spawn Working Directory",
"The directory to spawn the application from",
NULL,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_interface_install_property (iface,
g_param_spec_boxed ("spawn-env",
"Sysprofawn Environment",
"The environment for the spawn child",
G_TYPE_STRV,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
}
gdouble
sysprof_profiler_get_elapsed (SysprofProfiler *self)
{
gdouble value = 0.0;
g_return_val_if_fail (SYSPROF_IS_PROFILER (self), 0.0);
g_object_get (self, "elapsed", &value, NULL);
return value;
}
gboolean
sysprof_profiler_get_is_running (SysprofProfiler *self)
{
gboolean is_running = FALSE;
g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE);
g_object_get (self, "is-running", &is_running, NULL);
return is_running;
}
gboolean
sysprof_profiler_get_is_mutable (SysprofProfiler *self)
{
gboolean is_mutable = FALSE;
g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE);
g_object_get (self, "is-mutable", &is_mutable, NULL);
return is_mutable;
}
gboolean
sysprof_profiler_get_spawn_inherit_environ (SysprofProfiler *self)
{
gboolean spawn_inherit_environ = FALSE;
g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE);
g_object_get (self, "spawn-inherit-environ", &spawn_inherit_environ, NULL);
return spawn_inherit_environ;
}
void
sysprof_profiler_set_spawn_inherit_environ (SysprofProfiler *self,
gboolean spawn_inherit_environ)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_object_set (self, "spawn-inherit-environ", !!spawn_inherit_environ, NULL);
}
gboolean
sysprof_profiler_get_spawn (SysprofProfiler *self)
{
gboolean spawn = FALSE;
g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE);
g_object_get (self, "spawn", &spawn, NULL);
return spawn;
}
void
sysprof_profiler_set_spawn_cwd (SysprofProfiler *self,
const gchar *spawn_cwd)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_object_set (self, "spawn-cwd", spawn_cwd, NULL);
}
void
sysprof_profiler_set_spawn (SysprofProfiler *self,
gboolean spawn)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_object_set (self, "spawn", !!spawn, NULL);
}
gboolean
sysprof_profiler_get_whole_system (SysprofProfiler *self)
{
gboolean whole_system = FALSE;
g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE);
g_object_get (self, "whole-system", &whole_system, NULL);
return whole_system;
}
void
sysprof_profiler_set_whole_system (SysprofProfiler *self,
gboolean whole_system)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_object_set (self, "whole-system", !!whole_system, NULL);
}
void
sysprof_profiler_set_spawn_argv (SysprofProfiler *self,
const gchar * const *spawn_argv)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_object_set (self, "spawn-argv", spawn_argv, NULL);
}
void
sysprof_profiler_set_spawn_env (SysprofProfiler *self,
const gchar * const *spawn_env)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_object_set (self, "spawn-env", spawn_env, NULL);
}
void
sysprof_profiler_add_source (SysprofProfiler *self,
SysprofSource *source)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_return_if_fail (SYSPROF_IS_SOURCE (source));
SYSPROF_PROFILER_GET_IFACE (self)->add_source (self, source);
}
void
sysprof_profiler_set_writer (SysprofProfiler *self,
SysprofCaptureWriter *writer)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_return_if_fail (writer != NULL);
SYSPROF_PROFILER_GET_IFACE (self)->set_writer (self, writer);
}
SysprofCaptureWriter *
sysprof_profiler_get_writer (SysprofProfiler *self)
{
g_return_val_if_fail (SYSPROF_IS_PROFILER (self), NULL);
return SYSPROF_PROFILER_GET_IFACE (self)->get_writer (self);
}
void
sysprof_profiler_start (SysprofProfiler *self)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
SYSPROF_PROFILER_GET_IFACE (self)->start (self);
}
void
sysprof_profiler_stop (SysprofProfiler *self)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
SYSPROF_PROFILER_GET_IFACE (self)->stop (self);
}
void
sysprof_profiler_add_pid (SysprofProfiler *self,
GPid pid)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_return_if_fail (pid > -1);
SYSPROF_PROFILER_GET_IFACE (self)->add_pid (self, pid);
}
void
sysprof_profiler_remove_pid (SysprofProfiler *self,
GPid pid)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_return_if_fail (pid > -1);
SYSPROF_PROFILER_GET_IFACE (self)->remove_pid (self, pid);
}
const GPid *
sysprof_profiler_get_pids (SysprofProfiler *self,
guint *n_pids)
{
g_return_val_if_fail (SYSPROF_IS_PROFILER (self), NULL);
g_return_val_if_fail (n_pids != NULL, NULL);
return SYSPROF_PROFILER_GET_IFACE (self)->get_pids (self, n_pids);
}
void
sysprof_profiler_emit_failed (SysprofProfiler *self,
const GError *error)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_return_if_fail (error != NULL);
g_signal_emit (self, signals [FAILED], 0, error);
}
void
sysprof_profiler_emit_stopped (SysprofProfiler *self)
{
g_return_if_fail (SYSPROF_IS_PROFILER (self));
g_signal_emit (self, signals [STOPPED], 0);
}

View File

@ -1,191 +0,0 @@
/* sysprof-profiler.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include "sysprof-version-macros.h"
#include "sysprof-capture-writer.h"
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_PROFILER (sysprof_profiler_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (SysprofProfiler, sysprof_profiler, SYSPROF, PROFILER, GObject)
struct _SysprofProfilerInterface
{
GTypeInterface parent_interface;
/**
* SysprofProfiler::failed:
* @self: A #SysprofProfiler
* @reason: A #GError representing the reason for the failure
*
* This signal is emitted if the profiler failed. Note that
* #SysprofProfiler::stopped will also be emitted, but does not allow for
* receiving the error condition.
*/
void (*failed) (SysprofProfiler *self,
const GError *error);
/**
* SysprofProfiler::stopped:
* @self: A #SysprofProfiler
*
* This signal is emitted when a profiler is stopped. It will always be
* emitted after a sysprof_profiler_start() has been called, either after
* completion of sysprof_profiler_stop() or after a failure or after asynchronous
* completion of stopping.
*/
void (*stopped) (SysprofProfiler *self);
/**
* SysprofProfiler::add_source:
*
* Adds a source to the profiler.
*/
void (*add_source) (SysprofProfiler *profiler,
SysprofSource *source);
/**
* SysprofProfiler::set_writer:
*
* Sets the writer to use for the profiler.
*/
void (*set_writer) (SysprofProfiler *self,
SysprofCaptureWriter *writer);
/**
* SysprofProfiler::get_writer:
*
* Gets the writer that is being used to capture.
*
* Returns: (nullable) (transfer none): A #SysprofCaptureWriter.
*/
SysprofCaptureWriter *(*get_writer) (SysprofProfiler *self);
/**
* SysprofProfiler::start:
*
* Starts the profiler.
*/
void (*start) (SysprofProfiler *self);
/**
* SysprofProfiler::stop:
*
* Stops the profiler.
*/
void (*stop) (SysprofProfiler *self);
/**
* SysprofProfiler::add_pid:
*
* Add a pid to be profiled.
*/
void (*add_pid) (SysprofProfiler *self,
GPid pid);
/**
* SysprofProfiler::remove_pid:
*
* Remove a pid from the profiler. This will not be called after
* SysprofProfiler::start has been called.
*/
void (*remove_pid) (SysprofProfiler *self,
GPid pid);
/**
* SysprofProfiler::get_pids:
*
* Gets the pids that are part of this profiling session. If no pids
* have been specified, %NULL is returned.
*
* Returns: (nullable) (transfer none): An array of #GPid, or %NULL.
*/
const GPid *(*get_pids) (SysprofProfiler *self,
guint *n_pids);
};
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_emit_failed (SysprofProfiler *self,
const GError *error);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_emit_stopped (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
gdouble sysprof_profiler_get_elapsed (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_profiler_get_is_mutable (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_profiler_get_spawn_inherit_environ (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_set_spawn_inherit_environ (SysprofProfiler *self,
gboolean spawn_inherit_environ);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_profiler_get_whole_system (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_set_whole_system (SysprofProfiler *self,
gboolean whole_system);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_profiler_get_spawn (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_set_spawn (SysprofProfiler *self,
gboolean spawn);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_set_spawn_argv (SysprofProfiler *self,
const gchar * const *spawn_argv);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_set_spawn_env (SysprofProfiler *self,
const gchar * const *spawn_env);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_set_spawn_cwd (SysprofProfiler *self,
const gchar *cwd);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_add_source (SysprofProfiler *self,
SysprofSource *source);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_set_writer (SysprofProfiler *self,
SysprofCaptureWriter *writer);
SYSPROF_AVAILABLE_IN_ALL
SysprofCaptureWriter *sysprof_profiler_get_writer (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_profiler_get_is_running (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_start (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_stop (SysprofProfiler *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_add_pid (SysprofProfiler *self,
GPid pid);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_profiler_remove_pid (SysprofProfiler *self,
GPid pid);
SYSPROF_AVAILABLE_IN_ALL
const GPid *sysprof_profiler_get_pids (SysprofProfiler *self,
guint *n_pids);
G_END_DECLS

View File

@ -1,753 +0,0 @@
/* sysprof-proxy-source.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-proxy-source"
#include "config.h"
#include <errno.h>
#include <gio/gunixfdlist.h>
#include <sysprof.h>
#include "sysprof-platform.h"
#include "sysprof-proxy-source.h"
struct _SysprofProxySource
{
GObject parent_instance;
GCancellable *cancellable;
SysprofCaptureWriter *writer;
gchar *bus_name;
gchar *object_path;
GArray *pids;
GPtrArray *monitors;
GBusType bus_type;
guint stopping_count;
guint is_ready : 1;
guint has_started : 1;
guint is_whole_system : 1;
};
typedef struct
{
SysprofProxySource *self;
gchar *name;
} Peer;
typedef struct
{
SysprofProxySource *self;
GDBusConnection *bus;
gchar *name;
gchar *object_path;
gint fd;
guint needs_stop : 1;
} Monitor;
enum {
PROP_0,
PROP_BUS_NAME,
PROP_BUS_TYPE,
PROP_OBJECT_PATH,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static inline gint
steal_fd (gint *fd)
{
gint r = *fd;
*fd = -1;
return r;
}
static void
_g_weak_ref_free (GWeakRef *wr)
{
g_weak_ref_clear (wr);
g_slice_free (GWeakRef, wr);
}
static void
peer_free (Peer *peer)
{
g_assert (peer != NULL);
g_clear_object (&peer->self);
g_clear_pointer (&peer->name, g_free);
g_slice_free (Peer, peer);
}
static void
monitor_free (Monitor *monitor)
{
if (monitor == NULL)
return;
if (monitor->needs_stop)
g_dbus_connection_call (monitor->bus,
monitor->name,
monitor->object_path,
"org.gnome.Sysprof3.Profiler",
"Stop",
g_variant_new ("()"),
G_VARIANT_TYPE ("()"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
NULL, NULL, NULL);
if (monitor->fd != -1)
{
close (monitor->fd);
monitor->fd = -1;
}
g_clear_object (&monitor->self);
g_clear_object (&monitor->bus);
g_clear_pointer (&monitor->name, g_free);
g_clear_pointer (&monitor->object_path, g_free);
g_slice_free (Monitor, monitor);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (Peer, peer_free);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (Monitor, monitor_free);
static void
sysprof_proxy_source_prepare (SysprofSource *source)
{
g_assert (SYSPROF_IS_PROXY_SOURCE (source));
sysprof_source_emit_ready (source);
}
static gboolean
sysprof_proxy_source_get_is_ready (SysprofSource *source)
{
g_assert (SYSPROF_IS_PROXY_SOURCE (source));
return TRUE;
}
static void
sysprof_proxy_source_set_writer (SysprofSource *source,
SysprofCaptureWriter *writer)
{
SysprofProxySource *self = (SysprofProxySource *)source;
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
g_assert (writer != NULL);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
self->writer = sysprof_capture_writer_ref (writer);
}
static void
sysprof_proxy_source_take_monitor (SysprofProxySource *self,
Monitor *monitor)
{
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
g_assert (monitor != NULL);
g_assert (monitor->self == self);
g_assert (monitor->bus == NULL || G_IS_DBUS_CONNECTION (monitor->bus));
if (g_cancellable_is_cancelled (self->cancellable))
monitor_free (monitor);
else
g_ptr_array_add (self->monitors, g_steal_pointer (&monitor));
}
static void
sysprof_proxy_source_start_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GDBusConnection *bus = (GDBusConnection *)object;
g_autoptr(Monitor) monitor = user_data;
g_autoptr(GVariant) reply = NULL;
g_autoptr(GError) error = NULL;
SysprofProxySource *self;
g_assert (G_IS_DBUS_CONNECTION (bus));
g_assert (monitor != NULL);
g_assert (SYSPROF_IS_PROXY_SOURCE (monitor->self));
g_assert (G_IS_ASYNC_RESULT (result));
if (!(reply = g_dbus_connection_call_with_unix_fd_list_finish (bus, NULL, result, &error)))
{
g_dbus_error_strip_remote_error (error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
monitor->needs_stop = TRUE;
g_message ("Failure or no profiler available on peer %s: %s",
monitor->name, error->message);
return;
}
self = monitor->self;
monitor->needs_stop = TRUE;
sysprof_proxy_source_take_monitor (self, g_steal_pointer (&monitor));
}
static void
sysprof_proxy_source_monitor (SysprofProxySource *self,
GDBusConnection *bus,
const gchar *bus_name)
{
g_autoptr(GUnixFDList) fd_list = NULL;
g_autoptr(GError) error = NULL;
Monitor *monitor;
gint fd;
gint handle;
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
g_assert (G_IS_DBUS_CONNECTION (bus));
g_assert (bus_name != NULL);
if (g_cancellable_is_cancelled (self->cancellable))
return;
fd_list = g_unix_fd_list_new ();
if (-1 == (fd = sysprof_memfd_create ("[sysprof-proxy-capture]")) ||
-1 == (handle = g_unix_fd_list_append (fd_list, fd, &error)))
{
if (fd != -1)
close (fd);
g_warning ("Failed to create memfd for peer: %s", error->message);
return;
}
monitor = g_slice_new0 (Monitor);
monitor->self = g_object_ref (self);
monitor->bus = g_object_ref (bus);
monitor->name = g_strdup (bus_name);
monitor->object_path = g_strdup (self->object_path);
monitor->fd = fd;
g_dbus_connection_call_with_unix_fd_list (bus,
bus_name,
self->object_path,
"org.gnome.Sysprof3.Profiler",
"Start",
g_variant_new ("(a{sv}h)", NULL, handle),
G_VARIANT_TYPE ("()"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
fd_list,
self->cancellable,
sysprof_proxy_source_start_cb,
g_steal_pointer (&monitor));
}
static void
sysprof_proxy_source_get_pid_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GDBusConnection *bus = (GDBusConnection *)object;
g_autoptr(Peer) peer = user_data;
g_autoptr(GVariant) reply = NULL;
g_autoptr(GError) error = NULL;
guint32 pid = 0;
g_assert (G_IS_DBUS_CONNECTION (bus));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (peer != NULL);
g_assert (SYSPROF_IS_PROXY_SOURCE (peer->self));
if (!(reply = g_dbus_connection_call_finish (bus, result, &error)))
return;
g_variant_get (reply, "(u)", &pid);
/* If we don't care about this PID, then ignore it */
for (guint i = 0; i < peer->self->pids->len; i++)
{
if ((GPid)pid == g_array_index (peer->self->pids, GPid, i))
{
sysprof_proxy_source_monitor (peer->self, bus, peer->name);
return;
}
}
}
static void
sysprof_proxy_source_list_names_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GDBusConnection *bus = (GDBusConnection *)object;
g_autofree const gchar **names = NULL;
g_autoptr(SysprofProxySource) self = user_data;
g_autoptr(GVariant) reply = NULL;
g_autoptr(GError) error = NULL;
g_assert (G_IS_DBUS_CONNECTION (bus));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
if (!(reply = g_dbus_connection_call_finish (bus, result, &error)))
{
g_warning ("Failed to list D-Bus peer names: %s", error->message);
return;
}
g_variant_get (reply, "(^a&s)", &names);
for (guint i = 0; names[i] != NULL; i++)
{
Peer *peer;
peer = g_slice_new (Peer);
peer->self = g_object_ref (self);
peer->name = g_strdup (names[i]);
g_dbus_connection_call (bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetConnectionUnixProcessID",
g_variant_new ("(s)", names[i]),
G_VARIANT_TYPE ("(u)"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
self->cancellable,
sysprof_proxy_source_get_pid_cb,
g_steal_pointer (&peer));
}
}
static void
sysprof_proxy_source_name_owner_changed_cb (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *params,
gpointer user_data)
{
GWeakRef *wr = user_data;
g_autoptr(SysprofProxySource) self = NULL;
const gchar *name;
const gchar *old_name;
const gchar *new_name;
g_assert (G_IS_DBUS_CONNECTION (connection));
g_assert (params != NULL);
g_assert (g_variant_is_of_type (params, G_VARIANT_TYPE ("(sss)")));
g_assert (wr != NULL);
g_variant_get (params, "(&s&s&s)", &name, &old_name, &new_name);
if (!(self = g_weak_ref_get (wr)))
return;
if (self->bus_name != NULL && g_strcmp0 (name, self->bus_name) == 0)
sysprof_proxy_source_monitor (self, connection, new_name);
}
static void
sysprof_proxy_source_get_bus_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(SysprofProxySource) self = user_data;
g_autoptr(GDBusConnection) bus = NULL;
g_autoptr(GError) error = NULL;
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
if (!(bus = g_bus_get_finish (result, &error)))
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
sysprof_source_emit_failed (SYSPROF_SOURCE (self), error);
return;
}
if (self->bus_name != NULL && g_dbus_is_name (self->bus_name))
{
GWeakRef *wr;
/* Try to monitor immediately */
sysprof_proxy_source_monitor (self, bus, self->bus_name);
/* Watch for changes in case the program isn't started yet and
* we want to monitor it after it is available.
*/
wr = g_slice_new0 (GWeakRef);
g_weak_ref_init (wr, self);
g_dbus_connection_signal_subscribe (bus,
NULL,
"org.freedesktop.DBus",
"NameOwnerChanged",
NULL,
NULL,
0,
sysprof_proxy_source_name_owner_changed_cb,
g_steal_pointer (&wr),
(GDestroyNotify)_g_weak_ref_free);
}
if (self->pids->len > 0)
{
/* We need to query the processes that have been spawned to see
* if they have our proxy address associated with them. But first,
* we need to find what pids own what connection.
*/
g_dbus_connection_call (bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"ListNames",
g_variant_new ("()"),
G_VARIANT_TYPE ("(as)"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
self->cancellable,
sysprof_proxy_source_list_names_cb,
g_object_ref (self));
return;
}
}
static void
sysprof_proxy_source_start (SysprofSource *source)
{
SysprofProxySource *self = (SysprofProxySource *)source;
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
self->has_started = TRUE;
g_bus_get (self->bus_type,
self->cancellable,
sysprof_proxy_source_get_bus_cb,
g_object_ref (self));
}
static void
sysprof_proxy_source_cat (SysprofProxySource *self,
SysprofCaptureReader *reader)
{
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
g_assert (reader != NULL);
if (self->writer != NULL &&
!sysprof_capture_writer_cat (self->writer, reader))
{
int errsv = errno;
g_warning ("Failed to join reader: %s", g_strerror (errsv));
}
}
static void
sysprof_proxy_source_complete_monitor (SysprofProxySource *self,
Monitor *monitor)
{
g_autoptr(SysprofCaptureReader) reader = NULL;
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
g_assert (monitor != NULL);
g_assert (monitor->self == self);
if (!(reader = sysprof_capture_reader_new_from_fd (steal_fd (&monitor->fd))))
{
int errsv = errno;
g_warning ("Failed to load reader from peer FD: %s", g_strerror (errsv));
}
else
sysprof_proxy_source_cat (self, reader);
}
static void
sysprof_proxy_source_stop_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GDBusConnection *bus = (GDBusConnection *)object;
g_autoptr(Monitor) monitor = user_data;
g_autoptr(GVariant) reply = NULL;
g_autoptr(GError) error = NULL;
SysprofProxySource *self;
g_assert (G_IS_DBUS_CONNECTION (bus));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (monitor != NULL);
self = monitor->self;
reply = g_dbus_connection_call_finish (bus, result, &error);
monitor->needs_stop = FALSE;
sysprof_proxy_source_complete_monitor (self, monitor);
self->stopping_count--;
if (self->stopping_count == 0)
sysprof_source_emit_finished (SYSPROF_SOURCE (self));
}
static void
sysprof_proxy_source_stop (SysprofSource *source)
{
SysprofProxySource *self = (SysprofProxySource *)source;
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
g_cancellable_cancel (self->cancellable);
for (guint i = 0; i < self->monitors->len; i++)
{
g_autoptr(Monitor) monitor = g_ptr_array_index (self->monitors, i);
g_ptr_array_index (self->monitors, i) = NULL;
if (monitor->needs_stop)
{
self->stopping_count++;
g_dbus_connection_call (monitor->bus,
monitor->name,
monitor->object_path,
"org.gnome.Sysprof3.Profiler",
"Stop",
g_variant_new ("()"),
G_VARIANT_TYPE ("()"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
NULL,
sysprof_proxy_source_stop_cb,
monitor);
monitor = NULL; /* stolen */
}
else
{
sysprof_proxy_source_complete_monitor (self, monitor);
}
}
if (self->stopping_count == 0)
sysprof_source_emit_finished (source);
}
static void
sysprof_proxy_source_add_pid (SysprofSource *source,
GPid pid)
{
SysprofProxySource *self = (SysprofProxySource *)source;
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
g_assert (pid > 0);
if (!self->has_started)
self->is_whole_system = FALSE;
g_array_append_val (self->pids, pid);
}
static void
sysprof_proxy_source_serialize (SysprofSource *source,
GKeyFile *keyfile,
const gchar *group)
{
SysprofProxySource *self = (SysprofProxySource *)source;
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
g_assert (keyfile != NULL);
g_assert (group != NULL);
g_key_file_set_string (keyfile, group, "bus-name", self->bus_name ?: "");
g_key_file_set_string (keyfile, group, "object-path", self->object_path ?: "");
g_key_file_set_integer (keyfile, group, "bus-type", self->bus_type);
}
static void
sysprof_proxy_source_deserialize (SysprofSource *source,
GKeyFile *keyfile,
const gchar *group)
{
SysprofProxySource *self = (SysprofProxySource *)source;
gint bus_type;
g_assert (SYSPROF_IS_PROXY_SOURCE (self));
g_assert (keyfile != NULL);
g_assert (group != NULL);
g_clear_pointer (&self->bus_name, g_free);
g_clear_pointer (&self->object_path, g_free);
self->bus_name = g_key_file_get_string (keyfile, group, "bus-name", NULL);
self->object_path = g_key_file_get_string (keyfile, group, "object-path", NULL);
bus_type = g_key_file_get_integer (keyfile, group, "bus-type", NULL);
if (bus_type == G_BUS_TYPE_SESSION || bus_type == G_BUS_TYPE_SYSTEM)
self->bus_type = bus_type;
}
static void
source_iface_init (SysprofSourceInterface *iface)
{
iface->add_pid = sysprof_proxy_source_add_pid;
iface->prepare = sysprof_proxy_source_prepare;
iface->set_writer = sysprof_proxy_source_set_writer;
iface->get_is_ready = sysprof_proxy_source_get_is_ready;
iface->stop = sysprof_proxy_source_stop;
iface->start = sysprof_proxy_source_start;
iface->serialize = sysprof_proxy_source_serialize;
iface->deserialize = sysprof_proxy_source_deserialize;
}
G_DEFINE_TYPE_WITH_CODE (SysprofProxySource, sysprof_proxy_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
static void
sysprof_proxy_source_finalize (GObject *object)
{
SysprofProxySource *self = (SysprofProxySource *)object;
g_clear_pointer (&self->monitors, g_ptr_array_unref);
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
g_clear_pointer (&self->bus_name, g_free);
g_clear_pointer (&self->object_path, g_free);
g_clear_pointer (&self->pids, g_array_unref);
g_clear_object (&self->cancellable);
G_OBJECT_CLASS (sysprof_proxy_source_parent_class)->finalize (object);
}
static void
sysprof_proxy_source_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofProxySource *self = SYSPROF_PROXY_SOURCE (object);
switch (prop_id)
{
case PROP_BUS_TYPE:
g_value_set_enum (value, self->bus_type);
break;
case PROP_BUS_NAME:
g_value_set_string (value, self->bus_name);
break;
case PROP_OBJECT_PATH:
g_value_set_string (value, self->object_path);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_proxy_source_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofProxySource *self = SYSPROF_PROXY_SOURCE (object);
switch (prop_id)
{
case PROP_BUS_TYPE:
self->bus_type = g_value_get_enum (value);
break;
case PROP_BUS_NAME:
g_free (self->bus_name);
self->bus_name = g_value_dup_string (value);
break;
case PROP_OBJECT_PATH:
g_free (self->object_path);
self->object_path = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_proxy_source_class_init (SysprofProxySourceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_proxy_source_finalize;
object_class->get_property = sysprof_proxy_source_get_property;
object_class->set_property = sysprof_proxy_source_set_property;
properties [PROP_BUS_TYPE] =
g_param_spec_enum ("bus-type", NULL, NULL,
G_TYPE_BUS_TYPE,
G_BUS_TYPE_SESSION,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
properties [PROP_BUS_NAME] =
g_param_spec_string ("bus-name", NULL, NULL,
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
properties [PROP_OBJECT_PATH] =
g_param_spec_string ("object-path", NULL, NULL,
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_proxy_source_init (SysprofProxySource *self)
{
self->cancellable = g_cancellable_new ();
self->pids = g_array_new (FALSE, FALSE, sizeof (GPid));
self->monitors = g_ptr_array_new_with_free_func ((GDestroyNotify) monitor_free);
self->is_whole_system = TRUE;
self->bus_type = G_BUS_TYPE_SESSION;
}
SysprofSource *
sysprof_proxy_source_new (GBusType bus_type,
const gchar *bus_name,
const gchar *object_path)
{
SysprofProxySource *self;
g_return_val_if_fail (bus_type == G_BUS_TYPE_SESSION || bus_type == G_BUS_TYPE_SYSTEM, NULL);
g_return_val_if_fail (bus_name != NULL, NULL);
g_return_val_if_fail (object_path != NULL, NULL);
if (bus_name && !*bus_name)
bus_name = NULL;
if (object_path && !*object_path)
object_path = NULL;
self = g_object_new (SYSPROF_TYPE_PROXY_SOURCE,
"bus-type", bus_type,
"bus-name", bus_name,
"object-path", object_path,
NULL);
return SYSPROF_SOURCE (g_steal_pointer (&self));
}

View File

@ -1,39 +0,0 @@
/* sysprof-proxy-source.h
*
* Copyright 2019 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
*/
#pragma once
#include <gio/gio.h>
#include "sysprof-source.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_PROXY_SOURCE (sysprof_proxy_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofProxySource, sysprof_proxy_source, SYSPROF, PROXY_SOURCE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSource *sysprof_proxy_source_new (GBusType bus_type,
const gchar *bus_name,
const gchar *object_path);
G_END_DECLS

View File

@ -1,335 +0,0 @@
/* sysprof-selection.c
*
* Copyright 2016-2019 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
*/
#define G_LOG_DOMAIN "sysprof-selection"
#include "config.h"
#include "sysprof-selection.h"
struct _SysprofSelection
{
GObject parent_instance;
GArray *ranges;
};
typedef struct
{
gint64 begin;
gint64 end;
} Range;
G_DEFINE_TYPE (SysprofSelection, sysprof_selection, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_HAS_SELECTION,
N_PROPS
};
enum {
CHANGED,
N_SIGNALS
};
static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
static inline void
int64_swap (gint64 *a,
gint64 *b)
{
if (*a > *b)
{
gint64 tmp = *a;
*a = *b;
*b = tmp;
}
}
static void
sysprof_selection_finalize (GObject *object)
{
SysprofSelection *self = (SysprofSelection *)object;
g_clear_pointer (&self->ranges, g_array_unref);
G_OBJECT_CLASS (sysprof_selection_parent_class)->finalize (object);
}
static void
sysprof_selection_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofSelection *self = SYSPROF_SELECTION (object);
switch (prop_id)
{
case PROP_HAS_SELECTION:
g_value_set_boolean (value, sysprof_selection_get_has_selection (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_selection_class_init (SysprofSelectionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_selection_finalize;
object_class->get_property = sysprof_selection_get_property;
properties [PROP_HAS_SELECTION] =
g_param_spec_boolean ("has-selection",
"Has Selection",
"Has Selection",
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
/**
* SysprofSelection::changed:
*
* This signal is emitted when the selection has changed.
*/
signals [CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
static void
sysprof_selection_init (SysprofSelection *self)
{
self->ranges = g_array_new (FALSE, FALSE, sizeof (Range));
}
gboolean
sysprof_selection_get_has_selection (SysprofSelection *self)
{
g_return_val_if_fail (SYSPROF_IS_SELECTION (self), FALSE);
return self->ranges->len > 0;
}
/**
* sysprof_selection_foreach:
* @self: A #SysprofSelection
* @foreach_func: (scope call): a callback for each range
* @user_data: user data for @foreach_func
*
* Calls @foreach_func for every selected range.
*/
void
sysprof_selection_foreach (SysprofSelection *self,
SysprofSelectionForeachFunc foreach_func,
gpointer user_data)
{
g_return_if_fail (SYSPROF_IS_SELECTION (self));
g_return_if_fail (foreach_func != NULL);
for (guint i = 0; i < self->ranges->len; i++)
{
const Range *range = &g_array_index (self->ranges, Range, i);
foreach_func (self, range->begin, range->end, user_data);
}
}
static gint
range_compare (gconstpointer a,
gconstpointer b)
{
const Range *ra = a;
const Range *rb = b;
if (ra->begin < rb->begin)
return -1;
if (rb->begin < ra->begin)
return 1;
if (ra->end < rb->end)
return -1;
if (rb->end < ra->end)
return 1;
return 0;
}
static void
join_overlapping (GArray *ranges)
{
if (ranges->len > 1)
{
guint i = 0;
while (i < ranges->len - 1)
{
Range *range;
Range next;
range = &g_array_index (ranges, Range, i);
next = g_array_index (ranges, Range, i + 1);
if (range->end > next.begin)
{
range->end = next.end;
g_array_remove_index (ranges, i + 1);
}
else
{
i++;
}
}
}
}
void
sysprof_selection_select_range (SysprofSelection *self,
gint64 begin_time,
gint64 end_time)
{
Range range = { 0 };
g_return_if_fail (SYSPROF_IS_SELECTION (self));
int64_swap (&begin_time, &end_time);
range.begin = begin_time;
range.end = end_time;
g_array_append_val (self->ranges, range);
g_array_sort (self->ranges, range_compare);
join_overlapping (self->ranges);
if (self->ranges->len == 1)
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]);
g_signal_emit (self, signals [CHANGED], 0);
}
void
sysprof_selection_unselect_range (SysprofSelection *self,
gint64 begin,
gint64 end)
{
g_return_if_fail (SYSPROF_IS_SELECTION (self));
int64_swap (&begin, &end);
for (guint i = 0; i < self->ranges->len; i++)
{
const Range *range = &g_array_index (self->ranges, Range, i);
if (range->begin == begin && range->end == end)
{
g_array_remove_index (self->ranges, i);
if (self->ranges->len == 0)
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]);
g_signal_emit (self, signals [CHANGED], 0);
break;
}
}
}
void
sysprof_selection_unselect_all (SysprofSelection *self)
{
g_return_if_fail (SYSPROF_IS_SELECTION (self));
if (self->ranges->len > 0)
{
g_array_remove_range (self->ranges, 0, self->ranges->len);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]);
g_signal_emit (self, signals [CHANGED], 0);
}
}
gboolean
sysprof_selection_contains (SysprofSelection *self,
gint64 time_at)
{
if (self == NULL || self->ranges->len == 0)
return TRUE;
for (guint i = 0; i < self->ranges->len; i++)
{
const Range *range = &g_array_index (self->ranges, Range, i);
if (time_at >= range->begin && time_at <= range->end)
return TRUE;
}
return FALSE;
}
SysprofSelection *
sysprof_selection_copy (const SysprofSelection *self)
{
SysprofSelection *copy;
if (self == NULL)
return NULL;
copy = g_object_new (SYSPROF_TYPE_SELECTION, NULL);
for (guint i = 0; i < self->ranges->len; i++)
{
Range range = g_array_index (self->ranges, Range, i);
g_array_append_val (copy->ranges, range);
}
return copy;
}
guint
sysprof_selection_get_n_ranges (SysprofSelection *self)
{
g_return_val_if_fail (SYSPROF_IS_SELECTION (self), 0);
return self->ranges ? self->ranges->len : 0;
}
void
sysprof_selection_get_nth_range (SysprofSelection *self,
guint nth,
gint64 *begin_time,
gint64 *end_time)
{
Range r = {0};
g_return_if_fail (SYSPROF_IS_SELECTION (self));
if (self->ranges && nth < self->ranges->len)
r = g_array_index (self->ranges, Range, nth);
if (begin_time)
*begin_time = r.begin;
if (end_time)
*end_time = r.end;
}

View File

@ -1,72 +0,0 @@
/* sysprof-selection.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include <glib-object.h>
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_SELECTION (sysprof_selection_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofSelection, sysprof_selection, SYSPROF, SELECTION, GObject)
typedef void (*SysprofSelectionForeachFunc) (SysprofSelection *self,
gint64 begin_time,
gint64 end_time,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_selection_get_has_selection (SysprofSelection *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_selection_contains (SysprofSelection *self,
gint64 time_at);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_selection_select_range (SysprofSelection *self,
gint64 begin_time,
gint64 end_time);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_selection_unselect_range (SysprofSelection *self,
gint64 begin,
gint64 end);
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_selection_get_n_ranges (SysprofSelection *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_selection_get_nth_range (SysprofSelection *self,
guint nth,
gint64 *begin_time,
gint64 *end_time);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_selection_unselect_all (SysprofSelection *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_selection_foreach (SysprofSelection *self,
SysprofSelectionForeachFunc foreach_func,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
SysprofSelection *sysprof_selection_copy (const SysprofSelection *self);
G_END_DECLS

View File

@ -1,189 +0,0 @@
/* sysprof-source.c
*
* Copyright 2016-2019 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 "sysprof-source.h"
G_DEFINE_INTERFACE (SysprofSource, sysprof_source, G_TYPE_OBJECT)
enum {
FAILED,
FINISHED,
READY,
N_SIGNALS
};
static guint signals [N_SIGNALS];
static void
sysprof_source_default_init (SysprofSourceInterface *iface)
{
signals [FAILED] = g_signal_new ("failed",
G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_ERROR);
signals [FINISHED] = g_signal_new ("finished",
G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
signals [READY] = g_signal_new ("ready",
G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
void
sysprof_source_add_pid (SysprofSource *self,
GPid pid)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
g_return_if_fail (pid != FALSE);
if (SYSPROF_SOURCE_GET_IFACE (self)->add_pid)
SYSPROF_SOURCE_GET_IFACE (self)->add_pid (self, pid);
}
void
sysprof_source_emit_finished (SysprofSource *self)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
g_signal_emit (self, signals [FINISHED], 0);
}
void
sysprof_source_emit_failed (SysprofSource *self,
const GError *error)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
g_return_if_fail (error != NULL);
g_signal_emit (self, signals [FAILED], 0, error);
}
void
sysprof_source_emit_ready (SysprofSource *self)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
g_signal_emit (self, signals [READY], 0);
}
gboolean
sysprof_source_get_is_ready (SysprofSource *self)
{
g_return_val_if_fail (SYSPROF_IS_SOURCE (self), FALSE);
if (SYSPROF_SOURCE_GET_IFACE (self)->get_is_ready)
return SYSPROF_SOURCE_GET_IFACE (self)->get_is_ready (self);
return TRUE;
}
void
sysprof_source_prepare (SysprofSource *self)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
if (SYSPROF_SOURCE_GET_IFACE (self)->prepare)
SYSPROF_SOURCE_GET_IFACE (self)->prepare (self);
}
void
sysprof_source_set_writer (SysprofSource *self,
SysprofCaptureWriter *writer)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
g_return_if_fail (writer != NULL);
if (SYSPROF_SOURCE_GET_IFACE (self)->set_writer)
SYSPROF_SOURCE_GET_IFACE (self)->set_writer (self, writer);
}
void
sysprof_source_start (SysprofSource *self)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
if (SYSPROF_SOURCE_GET_IFACE (self)->start)
SYSPROF_SOURCE_GET_IFACE (self)->start (self);
}
void
sysprof_source_stop (SysprofSource *self)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
if (SYSPROF_SOURCE_GET_IFACE (self)->stop)
SYSPROF_SOURCE_GET_IFACE (self)->stop (self);
}
void
sysprof_source_modify_spawn (SysprofSource *self,
SysprofSpawnable *spawnable)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
g_return_if_fail (SYSPROF_IS_SPAWNABLE (spawnable));
if (SYSPROF_SOURCE_GET_IFACE (self)->modify_spawn)
SYSPROF_SOURCE_GET_IFACE (self)->modify_spawn (self, spawnable);
}
void
sysprof_source_serialize (SysprofSource *self,
GKeyFile *keyfile,
const gchar *group)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
g_return_if_fail (keyfile != NULL);
g_return_if_fail (group != NULL);
if (SYSPROF_SOURCE_GET_IFACE (self)->serialize)
SYSPROF_SOURCE_GET_IFACE (self)->serialize (self, keyfile, group);
}
void
sysprof_source_deserialize (SysprofSource *self,
GKeyFile *keyfile,
const gchar *group)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
g_return_if_fail (keyfile != NULL);
g_return_if_fail (group != NULL);
if (SYSPROF_SOURCE_GET_IFACE (self)->deserialize)
SYSPROF_SOURCE_GET_IFACE (self)->deserialize (self, keyfile, group);
}
void
sysprof_source_supplement (SysprofSource *self,
SysprofCaptureReader *reader)
{
g_return_if_fail (SYSPROF_IS_SOURCE (self));
g_return_if_fail (reader != NULL);
if (SYSPROF_SOURCE_GET_IFACE (self)->supplement)
SYSPROF_SOURCE_GET_IFACE (self)->supplement (self, reader);
}

View File

@ -1,212 +0,0 @@
/* sysprof-source.h
*
* Copyright 2016-2019 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
*/
#pragma once
#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
# error "Only <sysprof.h> can be included directly."
#endif
#include <glib.h>
#include <gio/gio.h>
#include <sysprof-capture.h>
#include "sysprof-spawnable.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_SOURCE (sysprof_source_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (SysprofSource, sysprof_source, SYSPROF, SOURCE, GObject)
struct _SysprofSourceInterface
{
GTypeInterface parent_iface;
/**
* SysprofSource::get_is_ready:
* @self: A SysprofSource.
*
* This function should return %TRUE if the source is ready to start
* profiling. If the source is not ready until after sysprof_source_start() has
* been called, use sysprof_source_emit_ready() to notify the profiler that the
* source is ready for profiling.
*
* Returns: %TRUE if the source is ready to start profiling.
*/
gboolean (*get_is_ready) (SysprofSource *self);
/**
* SysprofSource::set_writer:
* @self: A #SysprofSource.
* @writer: A #SysprofCaptureWriter
*
* Sets the #SysprofCaptureWriter to use when profiling. @writer is only safe to
* use from the main thread. If you need to capture from a thread, you should
* create a memory-based #SysprofCaptureWriter and then splice that into this
* writer from the main thread when profiling completes.
*
* See sysprof_capture_writer_splice() for information on splicing writers.
*/
void (*set_writer) (SysprofSource *self,
SysprofCaptureWriter *writer);
/**
* SysprofSource::prepare:
*
* This function is called before profiling has started. The source should
* prepare any pre-profiling setup here. It may perform this work
* asynchronously, but must g_object_notify() the SysprofSource::is-ready
* property once that asynchronous work has been performed. Until it
* is ready, #SysprofSource::is-ready must return FALSE.
*/
void (*prepare) (SysprofSource *self);
/**
* SysprofSource::add_pid:
* @self: A #SysprofSource
* @pid: A pid_t > -1
*
* This function is used to notify the #SysprofSource that a new process,
* identified by @pid, should be profiled. By default, sources should
* assume all processes, and only restrict to a given set of pids if
* this function is called.
*/
void (*add_pid) (SysprofSource *self,
GPid pid);
/**
* SysprofSource::start:
* @self: A #SysprofSource.
*
* Start profiling as configured.
*
* If a failure occurs while processing, the source should notify the
* profiling session via sysprof_source_emit_failed() from the main thread.
*/
void (*start) (SysprofSource *self);
/**
* SysprofSource::stop:
* @self: A #SysprofSource.
*
* Stop capturing a profile. The source should immediately stop
* profiling and perform any cleanup tasks required. If doing
* off-main-thread capturing, this is a good time to splice your
* capture into the capture file set with sysprof_source_set_writer().
*
* If you need to perform asynchronous cleanup, call
* sysprof_source_emit_finished() once that work has completed. If you do
* not need to perform asynchronous cleanup, call
* sysprof_source_emit_finished() from this function.
*
* sysprof_source_emit_finished() must be called from the main-thread.
*/
void (*stop) (SysprofSource *self);
/**
* SysprofSource::modify-spawn:
* @self: a #SysprofSource
* @spawnable: a #SysprofSpawnable
*
* Allows the source to modify the launcher or argv before the
* process is spawned.
*/
void (*modify_spawn) (SysprofSource *self,
SysprofSpawnable *spawnable);
/**
* SysprofSource::supplement:
*
* The "supplement" vfunc is called when a source should attempt to add
* any additional data to the trace file based on existing data within
* the trace file. A #SysprofCaptureReader is provided to simplify this
* process for the vfunc. It should write to the writer provided in
* sysprof_source_set_writer().
*
* This function must finish synchronously.
*/
void (*supplement) (SysprofSource *self,
SysprofCaptureReader *reader);
/**
* SysprofSource::serialize:
* @self: a #SysprofSource
* @keyfile: a #GKeyFile
* @group: the keyfile group to use
*
* Requests that the source serialize itself into the keyfile
* so that it may be replayed at a future point in time.
*/
void (*serialize) (SysprofSource *self,
GKeyFile *keyfile,
const gchar *group);
/**
* SysprofSource::deserialize:
* @self: a #SysprofSource
* @keyfile: a #GKeyFile
* @group: the keyfile group to use
*
* Deserialize from the saved state.
*/
void (*deserialize) (SysprofSource *self,
GKeyFile *keyfile,
const gchar *group);
};
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_add_pid (SysprofSource *self,
GPid pid);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_emit_ready (SysprofSource *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_emit_finished (SysprofSource *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_emit_failed (SysprofSource *self,
const GError *error);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_source_get_is_ready (SysprofSource *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_prepare (SysprofSource *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_set_writer (SysprofSource *self,
SysprofCaptureWriter *writer);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_start (SysprofSource *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_stop (SysprofSource *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_modify_spawn (SysprofSource *self,
SysprofSpawnable *spawnable);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_serialize (SysprofSource *self,
GKeyFile *keyfile,
const gchar *group);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_deserialize (SysprofSource *self,
GKeyFile *keyfile,
const gchar *group);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_source_supplement (SysprofSource *self,
SysprofCaptureReader *reader);
G_END_DECLS

View File

@ -1,340 +0,0 @@
/* sysprof-spawnable.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-spawnable"
#include "config.h"
#include <unistd.h>
#include "sysprof-spawnable.h"
typedef struct
{
gint dest_fd;
gint fd;
} FDMapping;
struct _SysprofSpawnable
{
GObject parent_instance;
GArray *fds;
GPtrArray *argv;
char **environ;
char *cwd;
gint next_fd;
GSubprocessFlags flags;
};
G_DEFINE_TYPE (SysprofSpawnable, sysprof_spawnable, G_TYPE_OBJECT)
/**
* sysprof_spawnable_new:
*
* Create a new #SysprofSpawnable.
*
* Returns: (transfer full): a newly created #SysprofSpawnable
*/
SysprofSpawnable *
sysprof_spawnable_new (void)
{
return g_object_new (SYSPROF_TYPE_SPAWNABLE, NULL);
}
/**
* sysprof_spawnable_get_flags:
* @self: a #SysprofSpawnable
*
* Gets the subprocess flags for spawning.
*
* Returns: the #GSubprocessFlags bitwise-or'd
*
* Since: 3.46
*/
GSubprocessFlags
sysprof_spawnable_get_flags (SysprofSpawnable *self)
{
g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), 0);
return self->flags;
}
/**
* sysprof_spawnable_set_flags:
* @self: a #SysprofSpawnable
*
* Set the flags to use when spawning the process.
*
* Since: 3.46
*/
void
sysprof_spawnable_set_flags (SysprofSpawnable *self,
GSubprocessFlags flags)
{
g_return_if_fail (SYSPROF_IS_SPAWNABLE (self));
self->flags = flags;
}
static void
fd_mapping_clear (gpointer data)
{
FDMapping *map = data;
if (map->fd != -1)
close (map->fd);
}
static void
sysprof_spawnable_finalize (GObject *object)
{
SysprofSpawnable *self = (SysprofSpawnable *)object;
g_clear_pointer (&self->fds, g_array_unref);
g_clear_pointer (&self->argv, g_ptr_array_unref);
g_clear_pointer (&self->environ, g_strfreev);
G_OBJECT_CLASS (sysprof_spawnable_parent_class)->finalize (object);
}
static void
sysprof_spawnable_class_init (SysprofSpawnableClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_spawnable_finalize;
}
static void
sysprof_spawnable_init (SysprofSpawnable *self)
{
self->next_fd = 3;
self->argv = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add (self->argv, NULL);
self->fds = g_array_new (FALSE, FALSE, sizeof (FDMapping));
g_array_set_clear_func (self->fds, fd_mapping_clear);
}
void
sysprof_spawnable_prepend_argv (SysprofSpawnable *self,
const gchar *argv)
{
g_return_if_fail (SYSPROF_IS_SPAWNABLE (self));
if (argv != NULL)
g_ptr_array_insert (self->argv, 0, g_strdup (argv));
}
void
sysprof_spawnable_append_argv (SysprofSpawnable *self,
const gchar *argv)
{
gint pos;
g_return_if_fail (SYSPROF_IS_SPAWNABLE (self));
if (argv == NULL)
return;
pos = self->argv->len - 1;
g_ptr_array_add (self->argv, NULL);
g_ptr_array_index (self->argv, pos) = g_strdup (argv);
}
void
sysprof_spawnable_append_args (SysprofSpawnable *self,
const gchar * const *args)
{
g_return_if_fail (SYSPROF_IS_SPAWNABLE (self));
if (args == NULL)
return;
for (guint i = 0; args[i]; i++)
sysprof_spawnable_append_argv (self, args[i]);
}
const gchar * const *
sysprof_spawnable_get_argv (SysprofSpawnable *self)
{
g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL);
return (const gchar * const *)(gpointer)self->argv->pdata;
}
const gchar * const *
sysprof_spawnable_get_environ (SysprofSpawnable *self)
{
g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL);
return (const gchar * const *)self->environ;
}
void
sysprof_spawnable_setenv (SysprofSpawnable *self,
const gchar *key,
const gchar *value)
{
g_return_if_fail (SYSPROF_IS_SPAWNABLE (self));
g_return_if_fail (key != NULL);
self->environ = g_environ_setenv (self->environ, key, value, TRUE);
}
void
sysprof_spawnable_set_environ (SysprofSpawnable *self,
const gchar * const *environ_)
{
g_return_if_fail (SYSPROF_IS_SPAWNABLE (self));
if (environ_ != (const gchar * const *)self->environ)
{
g_strfreev (self->environ);
self->environ = g_strdupv ((gchar **)environ_);
}
}
const gchar *
sysprof_spawnable_getenv (SysprofSpawnable *self,
const gchar *key)
{
g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL);
g_return_val_if_fail (key != NULL, NULL);
return g_environ_getenv (self->environ, key);
}
gint
sysprof_spawnable_take_fd (SysprofSpawnable *self,
gint fd,
gint dest_fd)
{
FDMapping map;
g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), -1);
if (dest_fd < 0)
dest_fd = self->next_fd++;
map.dest_fd = dest_fd;
map.fd = fd;
if (dest_fd >= self->next_fd)
self->next_fd = dest_fd + 1;
g_array_append_val (self->fds, map);
return dest_fd;
}
void
sysprof_spawnable_foreach_fd (SysprofSpawnable *self,
SysprofSpawnableFDForeach foreach,
gpointer user_data)
{
g_return_if_fail (SYSPROF_IS_SPAWNABLE (self));
g_return_if_fail (foreach != NULL);
for (guint i = 0; i < self->fds->len; i++)
{
const FDMapping *map = &g_array_index (self->fds, FDMapping, i);
foreach (map->dest_fd, map->fd, user_data);
}
}
/**
* sysprof_spawnable_set_starting_fd:
*
* Sets the next FD number to use when mapping a child FD. This helps
* in situations where the embedder knows that some lower-numbered FDs
* will be taken and therefore unknown to the spawnable.
*
* The default for this is 2.
*
* Since: 3.34
*/
void
sysprof_spawnable_set_starting_fd (SysprofSpawnable *self,
gint starting_fd)
{
g_return_if_fail (SYSPROF_IS_SPAWNABLE (self));
if (starting_fd < 0)
starting_fd = 2;
self->next_fd = starting_fd;
}
/**
* sysprof_spawnable_spawn:
*
* Creates a new subprocess using the configured options.
*
* Returns: (transfer full): a #GSubprocess or %NULL on failure and
* @error is set.
*
* Since: 3.34
*/
GSubprocess *
sysprof_spawnable_spawn (SysprofSpawnable *self,
GError **error)
{
g_autoptr(GSubprocessLauncher) launcher = NULL;
const gchar * const *argv;
g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL);
launcher = g_subprocess_launcher_new (self->flags);
g_subprocess_launcher_set_environ (launcher, self->environ);
if (self->cwd != NULL)
g_subprocess_launcher_set_cwd (launcher, self->cwd);
else
g_subprocess_launcher_set_cwd (launcher, g_get_home_dir ());
for (guint i = 0; i < self->fds->len; i++)
{
FDMapping *map = &g_array_index (self->fds, FDMapping, i);
g_subprocess_launcher_take_fd (launcher, map->fd, map->dest_fd);
map->fd = -1;
}
argv = sysprof_spawnable_get_argv (self);
return g_subprocess_launcher_spawnv (launcher, argv, error);
}
void
sysprof_spawnable_set_cwd (SysprofSpawnable *self,
const gchar *cwd)
{
g_return_if_fail (SYSPROF_IS_SPAWNABLE (self));
if (g_strcmp0 (cwd, self->cwd) != 0)
{
g_free (self->cwd);
self->cwd = g_strdup (cwd);
}
}

View File

@ -1,86 +0,0 @@
/* sysprof-spawnable.h
*
* Copyright 2019 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
*/
#pragma once
#include <gio/gio.h>
#include "sysprof-version-macros.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_SPAWNABLE (sysprof_spawnable_get_type())
typedef void (*SysprofSpawnableFDForeach) (gint dest_fd,
gint fd,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofSpawnable, sysprof_spawnable, SYSPROF, SPAWNABLE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSpawnable *sysprof_spawnable_new (void);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_spawnable_prepend_argv (SysprofSpawnable *self,
const gchar *argv);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_spawnable_append_argv (SysprofSpawnable *self,
const gchar *argv);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_spawnable_append_args (SysprofSpawnable *self,
const gchar * const *argv);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_spawnable_set_cwd (SysprofSpawnable *self,
const gchar *cwd);
SYSPROF_AVAILABLE_IN_ALL
const gchar * const *sysprof_spawnable_get_argv (SysprofSpawnable *self);
SYSPROF_AVAILABLE_IN_ALL
const gchar * const *sysprof_spawnable_get_environ (SysprofSpawnable *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_spawnable_set_environ (SysprofSpawnable *self,
const gchar * const *environ);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_spawnable_setenv (SysprofSpawnable *self,
const gchar *key,
const gchar *value);
SYSPROF_AVAILABLE_IN_ALL
const gchar *sysprof_spawnable_getenv (SysprofSpawnable *self,
const gchar *key);
SYSPROF_AVAILABLE_IN_ALL
gint sysprof_spawnable_take_fd (SysprofSpawnable *self,
gint fd,
gint dest_fd);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_spawnable_foreach_fd (SysprofSpawnable *self,
SysprofSpawnableFDForeach foreach,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_spawnable_set_starting_fd (SysprofSpawnable *self,
gint starting_fd);
SYSPROF_AVAILABLE_IN_ALL
GSubprocess *sysprof_spawnable_spawn (SysprofSpawnable *self,
GError **error);
SYSPROF_AVAILABLE_IN_3_46
GSubprocessFlags sysprof_spawnable_get_flags (SysprofSpawnable *self);
SYSPROF_AVAILABLE_IN_3_46
void sysprof_spawnable_set_flags (SysprofSpawnable *self,
GSubprocessFlags flags);
G_END_DECLS

View File

@ -1,593 +0,0 @@
/* sysprof-symbol-map.c
*
* Copyright 2019 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
*/
#define G_LOG_DOMAIN "sysprof-symbol-map"
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "sysprof-map-lookaside.h"
#include "sysprof-symbol-map.h"
/*
* Because we can't rely on the address ranges of symbols from ELF files
* or elsewhere, we have to duplicate a lot of entries when building this
* so that we can resolve all of the corrent addresses.
*/
SYSPROF_ALIGNED_BEGIN(1)
typedef struct
{
SysprofCaptureAddress addr_begin;
SysprofCaptureAddress addr_end;
guint32 pid;
guint32 offset;
guint32 tag_offset;
guint32 padding;
} Decoded
SYSPROF_ALIGNED_END(1);
struct _SysprofSymbolMap
{
/* For creating maps */
GStringChunk *chunk;
GHashTable *lookasides;
GPtrArray *resolvers;
GPtrArray *samples;
guint resolved : 1;
/* For reading maps */
GMappedFile *mapped;
const Decoded *symbols;
gsize n_symbols;
const gchar *beginptr;
const gchar *endptr;
};
typedef struct
{
SysprofCaptureAddress addr;
const gchar *name;
GQuark tag;
guint32 pid;
} Element;
static void
element_free (Element *ele)
{
g_slice_free (Element, ele);
}
static gint
element_compare (gconstpointer a,
gconstpointer b)
{
const Element *aa = *(const Element **)a;
const Element *bb = *(const Element **)b;
if (aa->pid < bb->pid)
return -1;
if (aa->pid > bb->pid)
return 1;
if (aa->addr < bb->addr)
return -1;
if (aa->addr > bb->addr)
return 1;
return 0;
}
static guint
element_hash (gconstpointer data)
{
const Element *ele = data;
struct {
guint32 a;
guint32 b;
} addr;
memcpy (&addr, &ele->addr, sizeof addr);
return addr.a ^ addr.b ^ ele->pid;
}
static gboolean
element_equal (gconstpointer a,
gconstpointer b)
{
const Element *aa = a;
const Element *bb = b;
return aa->pid == bb->pid && aa->addr == bb->addr;
}
SysprofSymbolMap *
sysprof_symbol_map_new (void)
{
SysprofSymbolMap *self;
self = g_slice_new0 (SysprofSymbolMap);
self->samples = g_ptr_array_new_with_free_func ((GDestroyNotify) element_free);
self->chunk = g_string_chunk_new (4096*16);
self->resolvers = g_ptr_array_new_with_free_func (g_object_unref);
self->lookasides = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) sysprof_map_lookaside_free);
return g_steal_pointer (&self);
}
void
sysprof_symbol_map_free (SysprofSymbolMap *self)
{
g_clear_pointer (&self->lookasides, g_hash_table_unref);
g_clear_pointer (&self->resolvers, g_ptr_array_unref);
g_clear_pointer (&self->chunk, g_string_chunk_free);
g_clear_pointer (&self->samples, g_ptr_array_unref);
g_clear_pointer (&self->mapped, g_mapped_file_unref);
self->beginptr = NULL;
self->endptr = NULL;
self->symbols = NULL;
self->n_symbols = 0;
g_slice_free (SysprofSymbolMap, self);
}
static gint
search_for_symbol_cb (gconstpointer a,
gconstpointer b)
{
const Decoded *key = a;
const Decoded *ele = b;
if (key->pid < ele->pid)
return -1;
if (key->pid > ele->pid)
return 1;
g_assert (key->pid == ele->pid);
if (key->addr_begin < ele->addr_begin)
return -1;
if (key->addr_begin > ele->addr_end)
return 1;
g_assert (key->addr_begin >= ele->addr_begin);
g_assert (key->addr_end <= ele->addr_end);
return 0;
}
const gchar *
sysprof_symbol_map_lookup (SysprofSymbolMap *self,
gint64 time,
gint32 pid,
SysprofCaptureAddress addr,
GQuark *tag)
{
const Decoded *ret;
const Decoded key = {
.addr_begin = addr,
.addr_end = addr,
.pid = pid,
.offset = 0,
.tag_offset = 0,
};
g_assert (self != NULL);
if (tag != NULL)
*tag = 0;
ret = bsearch (&key,
self->symbols,
self->n_symbols,
sizeof *ret,
search_for_symbol_cb);
if (ret == NULL || ret->offset == 0)
return NULL;
if (tag != NULL && ret->tag_offset > 0)
{
if (ret->tag_offset < (self->endptr - self->beginptr))
*tag = g_quark_from_string (&self->beginptr[ret->tag_offset]);
}
if (ret->offset < (self->endptr - self->beginptr))
return &self->beginptr[ret->offset];
return NULL;
}
void
sysprof_symbol_map_add_resolver (SysprofSymbolMap *self,
SysprofSymbolResolver *resolver)
{
g_assert (self != NULL);
g_assert (SYSPROF_IS_SYMBOL_RESOLVER (resolver));
g_ptr_array_add (self->resolvers, g_object_ref (resolver));
}
static gboolean
sysprof_symbol_map_do_alloc (SysprofSymbolMap *self,
SysprofCaptureReader *reader,
GHashTable *seen)
{
const SysprofCaptureAllocation *ev;
g_assert (self != NULL);
g_assert (reader != NULL);
g_assert (seen != NULL);
if (!(ev = sysprof_capture_reader_read_allocation (reader)))
return FALSE;
for (guint i = 0; i < ev->n_addrs; i++)
{
SysprofCaptureAddress addr = ev->addrs[i];
for (guint j = 0; j < self->resolvers->len; j++)
{
SysprofSymbolResolver *resolver = g_ptr_array_index (self->resolvers, j);
g_autofree gchar *name = NULL;
const gchar *cname;
Element ele;
GQuark tag = 0;
name = sysprof_symbol_resolver_resolve_with_context (resolver,
ev->frame.time,
ev->frame.pid,
SYSPROF_ADDRESS_CONTEXT_USER,
addr,
&tag);
if (name == NULL)
continue;
cname = g_string_chunk_insert_const (self->chunk, name);
ele.addr = addr;
ele.pid = ev->frame.pid;
ele.name = cname;
ele.tag = tag;
if (!g_hash_table_contains (seen, &ele))
{
Element *cpy = g_slice_dup (Element, &ele);
g_hash_table_add (seen, cpy);
g_ptr_array_add (self->samples, cpy);
}
}
}
return TRUE;
}
static gboolean
sysprof_symbol_map_do_sample (SysprofSymbolMap *self,
SysprofCaptureReader *reader,
GHashTable *seen)
{
SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE;
const SysprofCaptureSample *sample;
g_assert (self != NULL);
g_assert (reader != NULL);
g_assert (seen != NULL);
if (!(sample = sysprof_capture_reader_read_sample (reader)))
return FALSE;
for (guint i = 0; i < sample->n_addrs; i++)
{
SysprofCaptureAddress addr = sample->addrs[i];
SysprofAddressContext context;
if (sysprof_address_is_context_switch (addr, &context))
{
last_context = context;
continue;
}
/* Handle backtrace() style backtraces with no context switch */
if (last_context == SYSPROF_ADDRESS_CONTEXT_NONE)
last_context = SYSPROF_ADDRESS_CONTEXT_USER;
for (guint j = 0; j < self->resolvers->len; j++)
{
SysprofSymbolResolver *resolver = g_ptr_array_index (self->resolvers, j);
g_autofree gchar *name = NULL;
const gchar *cname;
Element ele;
GQuark tag = 0;
name = sysprof_symbol_resolver_resolve_with_context (resolver,
sample->frame.time,
sample->frame.pid,
last_context,
addr,
&tag);
if (name == NULL)
continue;
cname = g_string_chunk_insert_const (self->chunk, name);
ele.addr = addr;
ele.pid = sample->frame.pid;
ele.name = cname;
ele.tag = tag;
if (!g_hash_table_contains (seen, &ele))
{
Element *cpy = g_slice_dup (Element, &ele);
g_hash_table_add (seen, cpy);
g_ptr_array_add (self->samples, cpy);
}
}
}
return TRUE;
}
void
sysprof_symbol_map_resolve (SysprofSymbolMap *self,
SysprofCaptureReader *reader)
{
g_autoptr(GHashTable) seen = NULL;
SysprofCaptureFrameType type;
g_return_if_fail (self != NULL);
g_return_if_fail (self->resolved == FALSE);
g_return_if_fail (reader != NULL);
self->resolved = TRUE;
seen = g_hash_table_new (element_hash, element_equal);
sysprof_capture_reader_reset (reader);
for (guint i = 0; i < self->resolvers->len; i++)
{
sysprof_symbol_resolver_load (g_ptr_array_index (self->resolvers, i), reader);
sysprof_capture_reader_reset (reader);
}
while (sysprof_capture_reader_peek_type (reader, &type))
{
if (type == SYSPROF_CAPTURE_FRAME_SAMPLE)
{
if (!sysprof_symbol_map_do_sample (self, reader, seen))
break;
continue;
}
else if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION)
{
if (!sysprof_symbol_map_do_alloc (self, reader, seen))
break;
continue;
}
if (!sysprof_capture_reader_skip (reader))
break;
}
g_ptr_array_sort (self->samples, element_compare);
}
void
sysprof_symbol_map_printf (SysprofSymbolMap *self)
{
g_return_if_fail (self != NULL);
g_return_if_fail (self->samples != NULL);
for (guint i = 0; i < self->samples->len; i++)
{
Element *ele = g_ptr_array_index (self->samples, i);
if (ele->tag)
g_print ("%-5d: %"G_GUINT64_FORMAT": %s [%s]\n", ele->pid, ele->addr, ele->name, g_quark_to_string (ele->tag));
else
g_print ("%-5d: %"G_GUINT64_FORMAT": %s\n", ele->pid, ele->addr, ele->name);
}
}
static guint
get_string_offset (GByteArray *ar,
GHashTable *seen,
const gchar *str)
{
gpointer ret;
if (str == NULL)
return 0;
if G_UNLIKELY (!g_hash_table_lookup_extended (seen, str, NULL, &ret))
{
ret = GUINT_TO_POINTER (ar->len);
g_byte_array_append (ar, (guint8 *)str, strlen (str) + 1);
g_hash_table_insert (seen, (gpointer)str, ret);
}
return GPOINTER_TO_UINT (ret);
}
gboolean
sysprof_symbol_map_serialize (SysprofSymbolMap *self,
gint fd)
{
static const Decoded empty = {0};
SysprofCaptureAddress begin = 0;
g_autoptr(GByteArray) ar = NULL;
g_autoptr(GHashTable) seen = NULL;
g_autoptr(GArray) decoded = NULL;
gsize offset;
g_assert (self != NULL);
g_assert (fd != -1);
ar = g_byte_array_new ();
seen = g_hash_table_new (NULL, NULL);
decoded = g_array_new (FALSE, FALSE, sizeof (Decoded));
/* Add some empty space to both give us non-zero offsets and also ensure
* empty space between data.
*/
g_byte_array_append (ar, (guint8 *)&empty, sizeof empty);
for (guint i = 0; i < self->samples->len; i++)
{
Element *ele = g_ptr_array_index (self->samples, i);
Decoded dec;
if (begin == 0)
begin = ele->addr;
if ((i + 1) < self->samples->len)
{
Element *next = g_ptr_array_index (self->samples, i + 1);
if (ele->pid == next->pid && ele->name == next->name)
continue;
}
dec.padding = 0;
dec.addr_begin = begin;
dec.addr_end = ele->addr;
dec.pid = ele->pid;
dec.offset = get_string_offset (ar, seen, ele->name);
g_assert (!dec.offset || g_strcmp0 (ele->name, (gchar *)&ar->data[dec.offset]) == 0);
if (ele->tag)
{
const gchar *tagstr = g_quark_to_string (ele->tag);
dec.tag_offset = get_string_offset (ar, seen, tagstr);
g_assert (g_strcmp0 (tagstr, (gchar *)&ar->data[dec.tag_offset]) == 0);
}
else
dec.tag_offset = 0;
g_array_append_val (decoded, dec);
begin = 0;
}
offset = sizeof (Decoded) * (gsize)decoded->len;
for (guint i = 0; i < decoded->len; i++)
{
Decoded *dec = &g_array_index (decoded, Decoded, i);
if (dec->offset > 0)
dec->offset += offset;
if (dec->tag_offset > 0)
dec->tag_offset += offset;
}
if (write (fd, decoded->data, offset) != offset)
return FALSE;
if (write (fd, ar->data, ar->len) != ar->len)
return FALSE;
/* Aggressively release state now that we're finished */
if (self->samples->len)
g_ptr_array_remove_range (self->samples, 0, self->samples->len);
if (self->resolvers != NULL)
g_ptr_array_remove_range (self->resolvers, 0, self->resolvers->len);
g_string_chunk_clear (self->chunk);
g_hash_table_remove_all (self->lookasides);
lseek (fd, 0L, SEEK_SET);
return TRUE;
}
gboolean
sysprof_symbol_map_deserialize (SysprofSymbolMap *self,
gint byte_order,
gint fd)
{
g_autoptr(GError) error = NULL;
gboolean needs_swap = byte_order != G_BYTE_ORDER;
gchar *beginptr;
gchar *endptr;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (self->mapped == NULL, FALSE);
if (!(self->mapped = g_mapped_file_new_from_fd (fd, TRUE, &error)))
{
g_warning ("Failed to map file: %s\n", error->message);
return FALSE;
}
beginptr = g_mapped_file_get_contents (self->mapped);
endptr = beginptr + g_mapped_file_get_length (self->mapped);
/* Ensure trialing \0 */
if (endptr > beginptr)
*(endptr - 1) = 0;
for (gchar *ptr = beginptr;
ptr < endptr && (ptr + sizeof (Decoded)) < endptr;
ptr += sizeof (Decoded))
{
Decoded *sym = (Decoded *)ptr;
if (sym->addr_begin == 0 &&
sym->addr_end == 0 &&
sym->pid == 0 &&
sym->offset == 0)
{
self->symbols = (const Decoded *)beginptr;
self->n_symbols = sym - self->symbols;
break;
}
else if (needs_swap)
{
sym->addr_begin = GUINT64_SWAP_LE_BE (sym->addr_begin);
sym->addr_end = GUINT64_SWAP_LE_BE (sym->addr_end);
sym->pid = GUINT32_SWAP_LE_BE (sym->pid);
sym->offset = GUINT32_SWAP_LE_BE (sym->offset);
sym->tag_offset = GUINT32_SWAP_LE_BE (sym->tag_offset);
}
#if 0
g_print ("Added pid=%d begin=%p end=%p\n",
sym->pid, (gpointer)sym->addr_begin, (gpointer)sym->addr_end);
#endif
}
self->beginptr = beginptr;
self->endptr = endptr;
return TRUE;
}

View File

@ -1,49 +0,0 @@
/* sysprof-symbol-map.h
*
* Copyright 2019 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
*/
#pragma once
#include <sysprof-capture.h>
#include "sysprof-symbol-resolver.h"
G_BEGIN_DECLS
typedef struct _SysprofSymbolMap SysprofSymbolMap;
SysprofSymbolMap *sysprof_symbol_map_new (void);
void sysprof_symbol_map_add_resolver (SysprofSymbolMap *self,
SysprofSymbolResolver *resolver);
void sysprof_symbol_map_resolve (SysprofSymbolMap *self,
SysprofCaptureReader *reader);
const gchar *sysprof_symbol_map_lookup (SysprofSymbolMap *self,
gint64 time,
gint32 pid,
SysprofCaptureAddress addr,
GQuark *tag);
void sysprof_symbol_map_printf (SysprofSymbolMap *self);
gboolean sysprof_symbol_map_serialize (SysprofSymbolMap *self,
gint fd);
gboolean sysprof_symbol_map_deserialize (SysprofSymbolMap *self,
gint byte_order,
gint fd);
void sysprof_symbol_map_free (SysprofSymbolMap *self);
G_END_DECLS

View File

@ -1,31 +0,0 @@
/* sysprof-symbol-resolver-private.h
*
* Copyright 2021 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
*/
#pragma once
#include <glib.h>
#include <sysprof-capture.h>
G_BEGIN_DECLS
char *_sysprof_symbol_resolver_load_file (SysprofCaptureReader *reader,
const char *path);
G_END_DECLS

Some files were not shown because too many files have changed in this diff Show More