Skip to content

Commit

Permalink
lib/perf: add support for perf based profiling
Browse files Browse the repository at this point in the history
Signed-off-by: Balazs Scheidler <[email protected]>
  • Loading branch information
bazsi committed Dec 31, 2024
1 parent 6cf486f commit 1f0d5ce
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 1 deletion.
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ dnl Checks for programs.
AC_PROG_CC
AC_PROG_CC_C99
AM_PROG_CC_C_O
AM_PROG_AS
if test "x$ac_cv_prog_cc_c99" = "xno"; then
AC_MSG_ERROR([C99 standard compliant C compiler required. Try GCC 3.x or later.])
fi
Expand Down
4 changes: 3 additions & 1 deletion lib/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ include lib/logthrsource/Makefile.am
include lib/logthrdest/Makefile.am
include lib/signal-slot-connector/Makefile.am
include lib/multi-line/Makefile.am
include lib/perf/Makefile.am

LSNG_RELEASE = $(shell echo @PACKAGE_VERSION@ | cut -d. -f1,2)

Expand Down Expand Up @@ -307,7 +308,8 @@ lib_libsyslog_ng_la_SOURCES = \
$(multiline_sources) \
$(logthrsource_sources) \
$(logthrdest_sources) \
$(signal_slot_connector_sources)
$(signal_slot_connector_sources) \
$(perf_sources)

lib_libsyslog_ng_la_CFLAGS = \
$(AM_CFLAGS) \
Expand Down
3 changes: 3 additions & 0 deletions lib/apphook.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "timeutils/cache.h"
#include "multi-line/multi-line-factory.h"
#include "filterx/filterx-globals.h"
#include "perf/perf.h"

#include <iv.h>
#include <iv_work.h>
Expand Down Expand Up @@ -215,6 +216,7 @@ construct_nondumpable_logger(msg_fatal);
void
app_startup(void)
{
perf_global_init();
msg_init(FALSE);
iv_set_fatal_msg_handler(app_fatal);
iv_init();
Expand Down Expand Up @@ -293,6 +295,7 @@ app_shutdown(void)
hostname_global_deinit();
crypto_deinit();
msg_deinit();
perf_global_deinit();


/* NOTE: the iv_deinit() call should come here, but there's some exit
Expand Down
8 changes: 8 additions & 0 deletions lib/perf/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
perfincludedir = ${pkgincludedir}/perf

perfinclude_HEADERS = \
lib/perf/perf.h

perf_sources = \
lib/perf/perf.c \
lib/perf/trampoline.S
159 changes: 159 additions & 0 deletions lib/perf/perf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* Copyright (c) 2024 Axoflow
* Copyright (c) 2024 Balázs Scheidler <[email protected]>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As an additional exemption you are allowed to compile & link against the
* OpenSSL libraries as published by the OpenSSL project. See the file
* COPYING for details.
*
*/

#include "perf.h"
#include "messages.h"
#include <sys/mman.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>

typedef struct _PerfTrampolineArea
{
guint8 *area;
gsize size;
gsize code_size;
struct
{
gint num;
gint alloc;
} trampolines;
} PerfTrampolineArea;

extern void _perf_trampoline_func_start(void);
extern void _perf_trampoline_func_end(void);

#define PAGE_SIZE 4096
#define PAGE_MAX_INDEX ((PAGE_SIZE-1))

#define SIZE_TO_PAGES(s) (((s + PAGE_MAX_INDEX) & ~PAGE_MAX_INDEX) / PAGE_SIZE)
#define PAGE_TO_SIZE(p) (p * PAGE_SIZE)

#define MAX_TRAMPOLINES 16384

static gboolean
_allocate_trampoline_area(PerfTrampolineArea *self)
{
guint8 *start = (guint8 *) &_perf_trampoline_func_start;
guint8 *end = (guint8 *) &_perf_trampoline_func_end;

self->code_size = end - start;
gsize code_pages = SIZE_TO_PAGES(self->code_size * MAX_TRAMPOLINES);

self->size = PAGE_TO_SIZE(code_pages);
self->area = mmap(NULL,
self->size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (self->area == MAP_FAILED)
return FALSE;

self->trampolines.num = self->size / self->code_size;
self->trampolines.alloc = 0;

for (gint i = 0; i < self->trampolines.num; i++)
{
memcpy(self->area + i * self->code_size, start, self->code_size * sizeof(gchar));
}
return TRUE;
}

static gpointer
_generate_trampoline(PerfTrampolineArea *self, gpointer target_address)
{
if (self->trampolines.alloc >= self->trampolines.num)
return NULL;

gint res = mprotect(self->area, self->size, PROT_READ | PROT_WRITE);
if (res < 0)
return NULL;

guint8 *trampoline_start = self->area + (self->trampolines.alloc++) * self->code_size;
guint8 *trampoline_end = trampoline_start + self->code_size;
uintptr_t *value_p = (uintptr_t *) (trampoline_end - sizeof(target_address));
*value_p = (uintptr_t) target_address;

__builtin___clear_cache((gpointer) trampoline_start, (gpointer) trampoline_end);

res = mprotect(self->area, self->size, PROT_READ | PROT_EXEC);
if (res < 0)
return NULL;
return (gpointer) trampoline_start;
}

static gboolean
_is_trampoline_address(PerfTrampolineArea *self, guint8 *address)
{
return self->area <= address && self->area + self->size > address;
}

static gboolean
_save_symbol(gpointer address, gsize size, const gchar *symbol_name)
{
gchar filename[64];

g_snprintf(filename, sizeof(filename), "/tmp/perf-%d.map", (int) getpid());
FILE *mapfile = fopen(filename, "a");
if (!mapfile)
return FALSE;
fprintf(mapfile, "%0lx %ld %s\n", (uintptr_t) address, size, symbol_name);
fclose(mapfile);
return TRUE;
}

static PerfTrampolineArea trampolines;

gpointer
perf_generate_trampoline(gpointer target_address, const gchar *symbol_name)
{
gpointer t = _generate_trampoline(&trampolines, target_address);

if (!t)
{
msg_warning_once("WARNING: out of free trampoline slots",
evt_tag_int("max_trampolines", trampolines.trampolines.num));
return target_address;
}

_save_symbol(t, trampolines.code_size, symbol_name);
return t;
}

gboolean
perf_is_trampoline_address(gpointer address)
{
return _is_trampoline_address(&trampolines, address);
}

void
perf_global_init(void)
{
_allocate_trampoline_area(&trampolines);
}

void
perf_global_deinit(void)
{
/* we are never freeing the trampolines, as that would invalidate any
* function pointers that point into that area */
}
38 changes: 38 additions & 0 deletions lib/perf/perf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024 Axoflow
* Copyright (c) 2024 Balázs Scheidler <[email protected]>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As an additional exemption you are allowed to compile & link against the
* OpenSSL libraries as published by the OpenSSL project. See the file
* COPYING for details.
*
*/

#ifndef SYSLOG_NG_PERF_H_INCLUDED
#define SYSLOG_NG_PERF_H_INCLUDED

#include "syslog-ng.h"

gpointer perf_generate_trampoline(gpointer target_address, const gchar *symbol_name);
gboolean perf_is_trampoline_address(gpointer address);


void perf_global_init(void);
void perf_global_deinit(void);


#endif
19 changes: 19 additions & 0 deletions lib/perf/trampoline.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.text
.global _perf_trampoline_func_start
.align 16
_perf_trampoline_func_start:
#ifdef __x86_64__
endbr64
sub $8, %rsp
mov _perf_trampoline_target_address(%rip), %r11
call *%r11
add $8, %rsp
ret
.align 8
_perf_trampoline_target_address:
.quad 0x0
#endif // __x86_64__

.global _perf_trampoline_func_end
_perf_trampoline_func_end:
.section .note.GNU-stack,"",@progbits

0 comments on commit 1f0d5ce

Please sign in to comment.