Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01f823cf79 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,7 +1,5 @@
|
||||
build
|
||||
*.user
|
||||
version.h
|
||||
*.pyc
|
||||
*.todo
|
||||
packaging/make_ubuntu2.sh
|
||||
test_*.log
|
||||
*.pyc
|
||||
|
||||
@@ -62,17 +62,6 @@ else()
|
||||
set(BACKTRACE_L_FLAGS "")
|
||||
endif()
|
||||
|
||||
check_c_source_compiles(
|
||||
"#define print(x) _Generic((x), default : print_unknown)(x) \n void print_unknown(){} \n int main () { print(0); }"
|
||||
HAS_GENERIC)
|
||||
|
||||
if(HAS_GENERIC)
|
||||
add_definitions(-DHAS_GENERIC)
|
||||
set(CSTD "c11")
|
||||
else()
|
||||
set(CSTD "c99")
|
||||
endif(HAS_GENERIC)
|
||||
|
||||
if( ENABLE_RSVG )
|
||||
pkg_check_modules( RSVG librsvg-2.0>=2.14.0 )
|
||||
endif( ENABLE_RSVG )
|
||||
@@ -127,11 +116,11 @@ include_directories( ${PROJECT_BINARY_DIR}
|
||||
|
||||
set( SOURCES src/config.c
|
||||
src/panel.c
|
||||
src/util/server.c
|
||||
src/server.c
|
||||
src/main.c
|
||||
src/init.c
|
||||
src/util/signals.c
|
||||
src/util/tracing.c
|
||||
src/signals.c
|
||||
src/tracing.c
|
||||
src/mouse_actions.c
|
||||
src/drag_and_drop.c
|
||||
src/clock/clock.c
|
||||
@@ -157,9 +146,10 @@ set( SOURCES src/config.c
|
||||
src/util/timer.c
|
||||
src/util/cache.c
|
||||
src/util/color.c
|
||||
src/util/print.c
|
||||
src/util/gradient.c
|
||||
src/util/test.c
|
||||
src/util/addr2line.c
|
||||
src/util/print.c
|
||||
src/util/mem.c
|
||||
src/util/uevent.c
|
||||
src/util/window.c )
|
||||
|
||||
@@ -278,9 +268,14 @@ if( RT_LIBRARY )
|
||||
endif( RT_LIBRARY )
|
||||
|
||||
target_link_libraries( tint2 m )
|
||||
if(ENABLE_BACKTRACE)
|
||||
target_link_libraries( tint2 dl )
|
||||
target_link_libraries( tint2 z )
|
||||
target_link_libraries( tint2 bfd )
|
||||
endif(ENABLE_BACKTRACE)
|
||||
|
||||
add_dependencies( tint2 version )
|
||||
set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=${CSTD} ${ASAN_C_FLAGS} ${TRACING_C_FLAGS}" )
|
||||
set_target_properties( tint2 PROPERTIES COMPILE_FLAGS "-Wall -Wpointer-arith -fno-strict-aliasing -pthread -std=c11 ${ASAN_C_FLAGS} ${TRACING_C_FLAGS}" )
|
||||
set_target_properties( tint2 PROPERTIES LINK_FLAGS "-pthread -fno-strict-aliasing ${ASAN_L_FLAGS} ${BACKTRACE_L_FLAGS} ${TRACING_L_FLAGS}" )
|
||||
|
||||
install( TARGETS tint2 DESTINATION bin )
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
2017-12-20 16.0
|
||||
- Fixes:
|
||||
- Taskbar: `taskbar_distribute_size = 1` now playes well with `task_align = center` and
|
||||
`task_align = right` (issue #688)
|
||||
2017-11-10 master
|
||||
- Enhancements:
|
||||
- Added Spanish translation (contributed by Vicmz)
|
||||
- Executor: updated tooltip documentation (issue #676)
|
||||
- Systray: warn on duplicate config option systray_name_filter (issue #652)
|
||||
- Changed standard from C99 to C11 to support generic printing for unit tests
|
||||
|
||||
2017-11-05 15.3
|
||||
- Fixes:
|
||||
@@ -975,4 +971,3 @@ released tint-0.2
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
12
README.md
12
README.md
@@ -1,5 +1,5 @@
|
||||
# Latest stable release: 16.0
|
||||
Changes: https://gitlab.com/o9000/tint2/blob/16.0/ChangeLog
|
||||
# Latest stable release: 15.3
|
||||
Changes: https://gitlab.com/o9000/tint2/blob/15.3/ChangeLog
|
||||
|
||||
Documentation: [doc/tint2.md](doc/tint2.md)
|
||||
|
||||
@@ -8,7 +8,7 @@ Compile it with (after you install the [dependencies](https://gitlab.com/o9000/t
|
||||
```
|
||||
git clone https://gitlab.com/o9000/tint2.git
|
||||
cd tint2
|
||||
git checkout 16.0
|
||||
git checkout 15.3
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
@@ -61,9 +61,9 @@ tint2 is a simple panel/taskbar made for modern X window managers. It was specif
|
||||
|
||||
# Known issues
|
||||
|
||||
* Graphical glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA ([issue 595](https://gitlab.com/o9000/tint2/issues/595))
|
||||
* Window managers that do not follow exactly the EWMH specification might not interact well with tint2 ([issue 627](https://gitlab.com/o9000/tint2/issues/627)).
|
||||
* Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE).
|
||||
* Graphic glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA ([issue 595](https://gitlab.com/o9000/tint2/issues/595))
|
||||
* Window managers that do not follow exactly the EWMH specification might not interact well with tint2 (known issues for [awesome](https://gitlab.com/o9000/tint2/issues/385), [bspwm](https://gitlab.com/o9000/tint2/issues/524). [openbox-multihead](https://gitlab.com/o9000/tint2/issues/456))
|
||||
* Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE)
|
||||
|
||||
# How can I help out?
|
||||
|
||||
|
||||
@@ -199,9 +199,9 @@ pre {
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="latest-stable-release-16-0"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">16.0</span><a name="latest-stable-release-16-0" href="#latest-stable-release-16-0" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/16.0/ChangeLog">https://gitlab.com/o9000/tint2/blob/16.0/ChangeLog</a></p><p>Documentation: <a href="manual.html">manual.html</a></p><p>Compile it with (after you install the <a href="https://gitlab.com/o9000/tint2/wikis/Install#dependencies">dependencies</a>):</p><pre class="highlight plaintext"><code>git clone https://gitlab.com/o9000/tint2.git
|
||||
<h1 id="latest-stable-release-15-3"><span class="md2man-title">Latest</span> <span class="md2man-section">stable</span> <span class="md2man-date">release:</span> <span class="md2man-source">15.3</span><a name="latest-stable-release-15-3" href="#latest-stable-release-15-3" class="md2man-permalink" title="permalink"></a></h1><p>Changes: <a href="https://gitlab.com/o9000/tint2/blob/15.3/ChangeLog">https://gitlab.com/o9000/tint2/blob/15.3/ChangeLog</a></p><p>Documentation: <a href="manual.html">manual.html</a></p><p>Compile it with (after you install the <a href="https://gitlab.com/o9000/tint2/wikis/Install#dependencies">dependencies</a>):</p><pre class="highlight plaintext"><code>git clone https://gitlab.com/o9000/tint2.git
|
||||
cd tint2
|
||||
git checkout 16.0
|
||||
git checkout 15.3
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
@@ -239,9 +239,9 @@ update-mime-database /usr/local/share/mime
|
||||
</ul>
|
||||
<h1 id="known-issues">Known issues<a name="known-issues" href="#known-issues" class="md2man-permalink" title="permalink"></a></h1>
|
||||
<ul>
|
||||
<li>Graphical glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA (<a href="https://gitlab.com/o9000/tint2/issues/595">issue 595</a>)</li>
|
||||
<li>Window managers that do not follow exactly the EWMH specification might not interact well with tint2 (<a href="https://gitlab.com/o9000/tint2/issues/627">issue 627</a>).</li>
|
||||
<li>Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE).</li>
|
||||
<li>Graphic glitches on Intel graphics cards can be avoided by changing the acceleration method to UXA (<a href="https://gitlab.com/o9000/tint2/issues/595">issue 595</a>)</li>
|
||||
<li>Window managers that do not follow exactly the EWMH specification might not interact well with tint2 (known issues for <a href="https://gitlab.com/o9000/tint2/issues/385">awesome</a>, <a href="https://gitlab.com/o9000/tint2/issues/524">bspwm</a>. <a href="https://gitlab.com/o9000/tint2/issues/456">openbox-multihead</a>)</li>
|
||||
<li>Full transparency requires a compositor such as Compton (if not provided already by the window manager, as in Compiz/Unity, KDE or XFCE)</li>
|
||||
</ul>
|
||||
<h1 id="how-can-i-help-out">How can I help out?<a name="how-can-i-help-out" href="#how-can-i-help-out" class="md2man-permalink" title="permalink"></a></h1>
|
||||
<ul>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH TINT2 1 "2017\-12\-20" 16.0
|
||||
.TH TINT2 1 "2017\-11\-05" 15.3
|
||||
.SH NAME
|
||||
.PP
|
||||
tint2 \- lightweight panel/taskbar
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# TINT2 1 "2017-12-20" 16.0
|
||||
# TINT2 1 "2017-11-05" 15.3
|
||||
|
||||
## NAME
|
||||
tint2 - lightweight panel/taskbar
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "panel.h"
|
||||
#include "server.h"
|
||||
#include "signals.h"
|
||||
#include "test.h"
|
||||
#include "tooltip.h"
|
||||
#include "tracing.h"
|
||||
#include "uevent.h"
|
||||
@@ -49,9 +48,6 @@ void handle_cli_arguments(int argc, char **argv)
|
||||
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
||||
fprintf(stdout, "tint2 version %s\n", VERSION_STRING);
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "--test") == 0) {
|
||||
run_all_tests();
|
||||
exit(0);
|
||||
} else if (strcmp(argv[i], "-c") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
i++;
|
||||
|
||||
41
src/panel.c
41
src/panel.c
@@ -494,11 +494,9 @@ gboolean resize_panel(void *obj)
|
||||
}
|
||||
}
|
||||
|
||||
// Distribute the remaining size between taskbars
|
||||
// Distribute the remaining size between tasks
|
||||
if (num_tasks > 0) {
|
||||
int task_size = total_size / num_tasks;
|
||||
if (taskbar_alignment != ALIGN_LEFT)
|
||||
task_size = MIN(task_size, panel_horizontal ? panel_config.g_task.maximum_width : panel_config.g_task.maximum_height);
|
||||
for (int i = 0; i < panel->num_desktops; i++) {
|
||||
Taskbar *taskbar = &panel->taskbar[i];
|
||||
if (!taskbar->area.on_screen)
|
||||
@@ -515,43 +513,6 @@ gboolean resize_panel(void *obj)
|
||||
taskbar->area.height += task_size;
|
||||
}
|
||||
}
|
||||
int slack = total_size - task_size * num_tasks;
|
||||
if (taskbar_alignment == ALIGN_RIGHT) {
|
||||
for (int i = 0; i < panel->num_desktops; i++) {
|
||||
Taskbar *taskbar = &panel->taskbar[i];
|
||||
if (!taskbar->area.on_screen)
|
||||
continue;
|
||||
if (panel_horizontal)
|
||||
taskbar->area.width += slack;
|
||||
else
|
||||
taskbar->area.height += slack;
|
||||
break;
|
||||
}
|
||||
} else if (taskbar_alignment == ALIGN_CENTER) {
|
||||
slack /= 2;
|
||||
for (int i = 0; i < panel->num_desktops; i++) {
|
||||
Taskbar *taskbar = &panel->taskbar[i];
|
||||
if (!taskbar->area.on_screen)
|
||||
continue;
|
||||
if (panel_horizontal)
|
||||
taskbar->area.width += slack;
|
||||
else
|
||||
taskbar->area.height += slack;
|
||||
taskbar->area.alignment = ALIGN_RIGHT;
|
||||
break;
|
||||
}
|
||||
for (int i = panel->num_desktops - 1; i >= 0; i--) {
|
||||
Taskbar *taskbar = &panel->taskbar[i];
|
||||
if (!taskbar->area.on_screen)
|
||||
continue;
|
||||
if (panel_horizontal)
|
||||
taskbar->area.width += slack;
|
||||
else
|
||||
taskbar->area.height += slack;
|
||||
taskbar->area.alignment = ALIGN_LEFT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No tasks => expand the first visible taskbar
|
||||
for (int i = 0; i < panel->num_desktops; i++) {
|
||||
|
||||
@@ -24,7 +24,7 @@ set(SOURCES ../util/common.c
|
||||
../util/cache.c
|
||||
../util/timer.c
|
||||
../config.c
|
||||
../util/server.c
|
||||
../server.c
|
||||
../launcher/apps-common.c
|
||||
../launcher/icon-theme-common.c
|
||||
md4.c
|
||||
|
||||
238
src/util/addr2line.c
Normal file
238
src/util/addr2line.c
Normal file
@@ -0,0 +1,238 @@
|
||||
/* addr2line.c -- convert addresses to line number and function name
|
||||
Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
|
||||
2007, 2009 Free Software Foundation, Inc.
|
||||
Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
|
||||
|
||||
This file is part of GNU Binutils.
|
||||
|
||||
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, 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, 51 Franklin Street - Fifth Floor, Boston,
|
||||
MA 02110-1301, USA. */
|
||||
|
||||
/* Derived from objdump.c and nm.c by Ulrich.Lauther@mchp.siemens.de */
|
||||
|
||||
#ifdef ENABLE_EXECINFO
|
||||
|
||||
#include <bfd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "addr2line.h"
|
||||
#include "print.h"
|
||||
|
||||
static bfd_boolean unwind_inlines = 1; /* -i, unwind inlined functions. */
|
||||
static bfd_boolean with_addresses = 0; /* -a, show addresses. */
|
||||
static bfd_boolean with_functions = 1; /* -f, show function names. */
|
||||
static bfd_boolean do_demangle = 1; /* -C, demangle names. */
|
||||
static bfd_boolean base_names = 1; /* -s, strip directory names. */
|
||||
|
||||
typedef struct Lookup {
|
||||
char *exe_file_name;
|
||||
void *address;
|
||||
char *result;
|
||||
bfd *abfd;
|
||||
asymbol **syms;
|
||||
bfd_vma pc;
|
||||
const char *filename;
|
||||
const char *functionname;
|
||||
unsigned int line;
|
||||
unsigned int discriminator;
|
||||
bfd_boolean found;
|
||||
} Lookup;
|
||||
|
||||
static asymbol **slurp_symtab(bfd *);
|
||||
static void find_address_in_section(bfd *, asection *, void *unused);
|
||||
static void translate_address(Lookup *lookup);
|
||||
|
||||
static asymbol **slurp_symtab(bfd *abfd)
|
||||
{
|
||||
if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
|
||||
return NULL;
|
||||
|
||||
long storage = bfd_get_symtab_upper_bound(abfd);
|
||||
bfd_boolean dynamic = FALSE;
|
||||
if (storage == 0) {
|
||||
storage = bfd_get_dynamic_symtab_upper_bound(abfd);
|
||||
dynamic = TRUE;
|
||||
}
|
||||
if (storage < 0)
|
||||
return NULL;
|
||||
|
||||
asymbol **syms = (asymbol **)malloc(storage);
|
||||
long symcount;
|
||||
if (dynamic)
|
||||
symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
|
||||
else
|
||||
symcount = bfd_canonicalize_symtab(abfd, syms);
|
||||
if (symcount < 0)
|
||||
return syms;
|
||||
|
||||
// If there are no symbols left after canonicalization and
|
||||
// we have not tried the dynamic symbols then give them a go.
|
||||
if (symcount == 0 && !dynamic && (storage = bfd_get_dynamic_symtab_upper_bound(abfd)) > 0) {
|
||||
free(syms);
|
||||
syms = (asymbol **)malloc(storage);
|
||||
symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
|
||||
}
|
||||
return syms;
|
||||
}
|
||||
|
||||
static void find_address_in_section(bfd *abfd, asection *section, void *unused)
|
||||
{
|
||||
Lookup *lookup = (Lookup *)abfd->usrdata;
|
||||
|
||||
bfd_vma vma;
|
||||
bfd_size_type size;
|
||||
|
||||
if (lookup->found)
|
||||
return;
|
||||
|
||||
if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
|
||||
return;
|
||||
|
||||
vma = bfd_get_section_vma(abfd, section);
|
||||
if (lookup->pc < vma)
|
||||
return;
|
||||
|
||||
size = bfd_get_section_size(section);
|
||||
if (lookup->pc >= vma + size)
|
||||
return;
|
||||
|
||||
lookup->found = bfd_find_nearest_line_discriminator(abfd,
|
||||
section,
|
||||
lookup->syms,
|
||||
lookup->pc - vma,
|
||||
&lookup->filename,
|
||||
&lookup->functionname,
|
||||
&lookup->line,
|
||||
&lookup->discriminator);
|
||||
}
|
||||
|
||||
static void translate_address(Lookup *lookup)
|
||||
{
|
||||
Buffer *buffer = NULL;
|
||||
lookup->pc = (bfd_vma)lookup->address;
|
||||
|
||||
if (with_addresses) {
|
||||
char tmp[256];
|
||||
bfd_sprintf_vma(lookup->abfd, tmp, lookup->pc);
|
||||
buffer = buffer_printf(buffer, "0x%s: ", tmp);
|
||||
}
|
||||
|
||||
lookup->found = FALSE;
|
||||
bfd_map_over_sections(lookup->abfd, find_address_in_section, NULL);
|
||||
|
||||
if (!lookup->found) {
|
||||
if (with_functions)
|
||||
buffer = buffer_printf(buffer, "?? ");
|
||||
buffer = buffer_printf(buffer, "??:0");
|
||||
} else {
|
||||
while (1) {
|
||||
if (with_functions) {
|
||||
const char *name;
|
||||
char *alloc = NULL;
|
||||
|
||||
name = lookup->functionname;
|
||||
if (name == NULL || *name == '\0')
|
||||
name = "??";
|
||||
else if (do_demangle) {
|
||||
alloc = bfd_demangle(lookup->abfd, name, 3);
|
||||
if (alloc != NULL)
|
||||
name = alloc;
|
||||
}
|
||||
|
||||
buffer = buffer_printf(buffer, "%s", name);
|
||||
buffer = buffer_printf(buffer, " at ");
|
||||
|
||||
if (alloc != NULL)
|
||||
free(alloc);
|
||||
}
|
||||
|
||||
if (base_names && lookup->filename != NULL) {
|
||||
const char *h = strrchr(lookup->filename, '/');
|
||||
if (h != NULL)
|
||||
lookup->filename = h + 1;
|
||||
}
|
||||
|
||||
buffer = buffer_printf(buffer, "%s:", lookup->filename ? lookup->filename : "??");
|
||||
if (lookup->line != 0) {
|
||||
if (lookup->discriminator != 0)
|
||||
buffer = buffer_printf(buffer, "%u (discriminator %u)", lookup->line, lookup->discriminator);
|
||||
else
|
||||
buffer = buffer_printf(buffer, "%u", lookup->line);
|
||||
} else {
|
||||
buffer = buffer_printf(buffer, "??");
|
||||
}
|
||||
if (!unwind_inlines)
|
||||
lookup->found = FALSE;
|
||||
else
|
||||
lookup->found =
|
||||
bfd_find_inliner_info(lookup->abfd, &lookup->filename, &lookup->functionname, &lookup->line);
|
||||
if (!lookup->found)
|
||||
break;
|
||||
buffer = buffer_printf(buffer, " (inlined by) ");
|
||||
}
|
||||
}
|
||||
if (buffer)
|
||||
lookup->result = buffer->data;
|
||||
}
|
||||
|
||||
Lookup *addr2line_init(const char *file_name)
|
||||
{
|
||||
bfd *abfd = bfd_openr(file_name, NULL);
|
||||
if (abfd == NULL)
|
||||
return NULL;
|
||||
|
||||
// Decompress sections.
|
||||
abfd->flags |= BFD_DECOMPRESS;
|
||||
|
||||
if (bfd_check_format(abfd, bfd_archive))
|
||||
goto err;
|
||||
|
||||
char **matching = NULL;
|
||||
if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
|
||||
free(matching);
|
||||
goto err;
|
||||
}
|
||||
|
||||
Lookup *lookup = (Lookup *)calloc(1, sizeof(Lookup));
|
||||
lookup->abfd = abfd;
|
||||
lookup->abfd->usrdata = lookup;
|
||||
lookup->syms = slurp_symtab(abfd);
|
||||
lookup->exe_file_name = strdup(file_name);
|
||||
return lookup;
|
||||
|
||||
err:
|
||||
bfd_close(abfd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void addr2line_destroy(Lookup *lookup)
|
||||
{
|
||||
free(lookup->syms);
|
||||
bfd_close(lookup->abfd);
|
||||
free(lookup->exe_file_name);
|
||||
free(lookup);
|
||||
}
|
||||
|
||||
char *addr2line_lookup(Lookup *lookup, void *address)
|
||||
{
|
||||
lookup->address = address;
|
||||
lookup->result = NULL;
|
||||
translate_address(lookup);
|
||||
return lookup->result;
|
||||
}
|
||||
|
||||
#endif
|
||||
14
src/util/addr2line.h
Normal file
14
src/util/addr2line.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef ADDR2LINE
|
||||
#define ADDR2LINE
|
||||
|
||||
#ifdef ENABLE_EXECINFO
|
||||
|
||||
typedef struct Lookup Lookup;
|
||||
|
||||
Lookup *addr2line_init(const char *file_name);
|
||||
void addr2line_destroy(Lookup *lookup);
|
||||
char *addr2line_lookup(Lookup *lookup, void *address);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,17 +0,0 @@
|
||||
#ifndef BOOL_H
|
||||
#define BOOL_H
|
||||
|
||||
#ifndef bool
|
||||
#define bool int
|
||||
#define false 0
|
||||
#define true 1
|
||||
#endif
|
||||
|
||||
#define SUCCESS true
|
||||
#define FAILURE false
|
||||
|
||||
#ifndef Status
|
||||
typedef int Status;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef COLORS_H
|
||||
#define COLORS_H
|
||||
|
||||
#define GREEN "\033[1;32m"
|
||||
#define YELLOW "\033[1;33m"
|
||||
#define RED "\033[1;31m"
|
||||
#define BLUE "\033[1;34m"
|
||||
#define RESET "\033[0m"
|
||||
|
||||
#endif
|
||||
@@ -31,7 +31,7 @@
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include "common.h"
|
||||
#include "server.h"
|
||||
#include "../server.h"
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
|
||||
@@ -12,7 +12,12 @@
|
||||
#include <Imlib2.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include "area.h"
|
||||
#include "colors.h"
|
||||
|
||||
#define GREEN "\033[1;32m"
|
||||
#define YELLOW "\033[1;33m"
|
||||
#define RED "\033[1;31m"
|
||||
#define BLUE "\033[1;34m"
|
||||
#define RESET "\033[0m"
|
||||
|
||||
#define MAX3(a, b, c) MAX(MAX(a, b), c)
|
||||
#define MIN3(a, b, c) MIN(MIN(a, b), c)
|
||||
|
||||
471
src/util/mem.c
Normal file
471
src/util/mem.c
Normal file
@@ -0,0 +1,471 @@
|
||||
#ifdef ENABLE_EXECINFO
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <execinfo.h>
|
||||
#include <pthread.h>
|
||||
#include <stdc-predef.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "addr2line.h"
|
||||
#include "mem.h"
|
||||
#include "print.h"
|
||||
|
||||
#ifndef RTLD_NEXT
|
||||
# define RTLD_NEXT ((void *) -1l)
|
||||
#endif
|
||||
|
||||
#ifndef thread_local
|
||||
# if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
|
||||
# define thread_local _Thread_local
|
||||
# elif defined _WIN32 && ( \
|
||||
defined _MSC_VER || \
|
||||
defined __ICL || \
|
||||
defined __DMC__ || \
|
||||
defined __BORLANDC__ )
|
||||
# define thread_local __declspec(thread)
|
||||
/* note that ICC (linux) and Clang are covered by __GNUC__ */
|
||||
# elif defined __GNUC__ || \
|
||||
defined __SUNPRO_C || \
|
||||
defined __xlC__
|
||||
# define thread_local __thread
|
||||
# else
|
||||
# error "Cannot define thread_local"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define get_caller() __builtin_extract_return_addr(__builtin_return_address(0))
|
||||
|
||||
#define size_t_mul_overflow(a, b) (a > 0 && b > SIZE_MAX / a)
|
||||
|
||||
typedef struct ListItem {
|
||||
struct ListItem *next;
|
||||
struct ListItem *prev;
|
||||
void *data;
|
||||
} ListItem;
|
||||
|
||||
typedef struct List {
|
||||
ListItem *head;
|
||||
ListItem *tail;
|
||||
} List;
|
||||
|
||||
void list_append(List *list, void *data)
|
||||
{
|
||||
ListItem *n = (ListItem*)calloc(1, sizeof(ListItem));
|
||||
n->data = data;
|
||||
if (!list->tail) {
|
||||
list->head = list->tail = n;
|
||||
} else {
|
||||
list->tail->next = n;
|
||||
n->prev = list->tail;
|
||||
list->tail = list->tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
typedef size_t (*HashFunc)(void *data);
|
||||
typedef int (*EqualFunc)(void *data1, void *data2);
|
||||
typedef void *(*CopyFunc)(void *data);
|
||||
|
||||
typedef struct HashTable {
|
||||
List *buckets;
|
||||
size_t num_buckets;
|
||||
size_t (*hash_func)(void *data);
|
||||
int (*equal_func)(void *data1, void *data2);
|
||||
void *(*copy_func)(void *data);
|
||||
} HashTable;
|
||||
|
||||
size_t hash_void(void *data)
|
||||
{
|
||||
return (size_t)data;
|
||||
}
|
||||
|
||||
int equal_void(void *data1, void *data2)
|
||||
{
|
||||
return data1 == data2;
|
||||
}
|
||||
|
||||
void *copy_void(void *data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
void hash_table_init(HashTable *table, size_t num_buckets, HashFunc hash_func, EqualFunc equal_func, CopyFunc copy_func)
|
||||
{
|
||||
if (table->buckets)
|
||||
return;
|
||||
if (num_buckets < 1)
|
||||
num_buckets = 4096;
|
||||
table->num_buckets = num_buckets;
|
||||
table->buckets = (List*)calloc(table->num_buckets, sizeof(List));
|
||||
table->hash_func = hash_func ? hash_func : hash_void;
|
||||
table->equal_func = equal_func ? equal_func : equal_void;
|
||||
table->copy_func = copy_func ? copy_func : copy_void;
|
||||
}
|
||||
|
||||
void *hash_table_add(HashTable *table, void *data)
|
||||
{
|
||||
size_t hash = table->hash_func(data);
|
||||
size_t bucket = hash % table->num_buckets;
|
||||
for (ListItem *existing = table->buckets[bucket].head; existing; existing = existing->next) {
|
||||
if (table->equal_func(existing->data, data))
|
||||
return existing->data;
|
||||
}
|
||||
list_append(&table->buckets[bucket], table->copy_func(data));
|
||||
return data;
|
||||
}
|
||||
|
||||
#define MAX_TRACE_SIZE 120
|
||||
|
||||
typedef struct Trace {
|
||||
void *entries[MAX_TRACE_SIZE+8];
|
||||
size_t id;
|
||||
} Trace;
|
||||
|
||||
#define LARGE_THRESH (10 * 1024 * 1024)
|
||||
|
||||
static void *(*calloc_original)(size_t count, size_t size) = NULL;
|
||||
static void *(*realloc_original)(void *p, size_t size) = NULL;
|
||||
static void *(*malloc_original)(size_t size) = NULL;
|
||||
static void *(*free_original)(void *p) = NULL;
|
||||
static gzFile mem_log = NULL;
|
||||
static pid_t mem_log_owner = 0;
|
||||
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static HashTable traces;
|
||||
static size_t next_trace_id = 0;
|
||||
pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static HashTable addr2lines;
|
||||
pthread_mutex_t addr2lines_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
thread_local int paused = 0;
|
||||
|
||||
static double get_unix_time()
|
||||
{
|
||||
struct timeval tv;
|
||||
if (gettimeofday(&tv, NULL) != 0)
|
||||
return 0;
|
||||
return tv.tv_sec + 1.0e-6 * tv.tv_usec;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static void get_backtrace(Trace *trace) {
|
||||
int size = backtrace(trace->entries, MAX_TRACE_SIZE);
|
||||
trace->entries[size] = NULL;
|
||||
for (void **p = trace->entries; *p; p++) {
|
||||
*p = *(p+2);
|
||||
}
|
||||
trace->id = 0;
|
||||
}
|
||||
|
||||
int trace_size(Trace *trace)
|
||||
{
|
||||
int size = 0;
|
||||
for (void **p = trace->entries; *p; p++) {
|
||||
size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
typedef struct FilenameLookup {
|
||||
char *file_name;
|
||||
Lookup *lookup;
|
||||
} FilenameLookup;
|
||||
|
||||
size_t hash_lookup(void *data)
|
||||
{
|
||||
FilenameLookup *lookup = (FilenameLookup *)data;
|
||||
size_t seed = 14695981039346656037ULL;
|
||||
size_t hash = seed;
|
||||
for (char *p = lookup->file_name; *p; p++) {
|
||||
hash = (hash ^ (size_t)(*p)) * 1099511628211ULL;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
int equal_lookup(void *data1, void *data2)
|
||||
{
|
||||
FilenameLookup *lookup1 = (FilenameLookup *)data1;
|
||||
FilenameLookup *lookup2 = (FilenameLookup *)data2;
|
||||
return strcmp(lookup1->file_name, lookup2->file_name) == 0;
|
||||
}
|
||||
|
||||
char *resolve_symbol(char *file_name, void *address)
|
||||
{
|
||||
FilenameLookup *lookup = (FilenameLookup *)calloc(1, sizeof(FilenameLookup));
|
||||
lookup->file_name = strdup(file_name);
|
||||
|
||||
pthread_mutex_lock(&addr2lines_mutex);
|
||||
hash_table_init(&addr2lines, 0, hash_lookup, equal_lookup, NULL);
|
||||
FilenameLookup *existing = (FilenameLookup *)hash_table_add(&addr2lines, lookup);
|
||||
if (existing == lookup) {
|
||||
lookup->lookup = addr2line_init(lookup->file_name);
|
||||
} else {
|
||||
free(lookup->file_name);
|
||||
free(lookup);
|
||||
}
|
||||
char *result = addr2line_lookup(existing->lookup, address);
|
||||
pthread_mutex_unlock(&addr2lines_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
char *backtrace_to_string(Trace *trace)
|
||||
{
|
||||
int size = trace_size(trace);
|
||||
char **strings = backtrace_symbols(trace->entries, size);
|
||||
if (!strings)
|
||||
return NULL;
|
||||
Buffer *buffer = NULL;
|
||||
buffer = buffer_printf(buffer, "t %lu ", trace->id);
|
||||
for (int i = 0; i < size; i++) {
|
||||
buffer = buffer_printf(buffer, "%s", strings[i]);
|
||||
// /lib/x86_64-linux-gnu/libglib-2.0.so.0(g_realloc+0xf) [0x7efda85c96af]
|
||||
char *func_start = strstr(strings[i], "(");
|
||||
if (func_start) {
|
||||
*func_start = 0;
|
||||
char *file_name = strings[i];
|
||||
char *resolved = resolve_symbol(file_name, trace->entries[i]);
|
||||
if (resolved) {
|
||||
buffer = buffer_printf(buffer, " %s", resolved);
|
||||
free(resolved);
|
||||
}
|
||||
}
|
||||
buffer = buffer_printf(buffer, "|");
|
||||
}
|
||||
free(strings);
|
||||
buffer = buffer_printf(buffer, "\n");
|
||||
return buffer->data;
|
||||
}
|
||||
|
||||
static size_t hash_trace(Trace *trace)
|
||||
{
|
||||
size_t seed = 14695981039346656037ULL;
|
||||
size_t hash = seed;
|
||||
for (void **p = trace->entries; *p; p++) {
|
||||
hash = (hash ^ (size_t)(*p)) * 1099511628211ULL;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static int equal_traces(Trace *a, Trace *b)
|
||||
{
|
||||
void **pa, **pb;
|
||||
for (pa = a->entries, pb = b->entries; *pa && *pb; pa++, pb++) {
|
||||
if (*pa != *pb)
|
||||
return 0;
|
||||
}
|
||||
if (*pa || *pb)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static Trace *copy_trace(Trace *trace)
|
||||
{
|
||||
Trace *result = (Trace*)calloc(1, sizeof(Trace));
|
||||
*result = *trace;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int trace_assign_id(Trace *trace)
|
||||
{
|
||||
int created = 0;
|
||||
pthread_mutex_lock(&trace_mutex);
|
||||
hash_table_init(&traces, 1024 * 1024, (HashFunc)hash_trace, (EqualFunc)equal_traces, (CopyFunc)copy_trace);
|
||||
trace->id = 0;
|
||||
Trace *existing = (Trace*)hash_table_add(&traces, trace);
|
||||
if (!existing->id) {
|
||||
next_trace_id++;
|
||||
existing->id = trace->id = next_trace_id;
|
||||
created = 1;
|
||||
} else {
|
||||
trace->id = existing->id;
|
||||
}
|
||||
pthread_mutex_unlock(&trace_mutex);
|
||||
return created;
|
||||
}
|
||||
|
||||
static void log_flush();
|
||||
|
||||
static void log_init()
|
||||
{
|
||||
char path[256];
|
||||
sprintf(path, "/tmp/tint2.%lu.memtrace", (size_t)getpid());
|
||||
pthread_mutex_lock(&log_mutex);
|
||||
if (!mem_log) {
|
||||
mem_log = gzopen(path, "w");
|
||||
mem_log_owner = getpid();
|
||||
atexit(log_flush);
|
||||
}
|
||||
pthread_mutex_unlock(&log_mutex);
|
||||
if (mem_log)
|
||||
fprintf(stderr, "Writing memory log to %s\n", path);
|
||||
}
|
||||
|
||||
static void log_write(char *buffer)
|
||||
{
|
||||
if (mem_log) {
|
||||
pthread_mutex_lock(&log_mutex);
|
||||
if (getpid() == mem_log_owner)
|
||||
gzwrite(mem_log, buffer, (unsigned)strlen(buffer));
|
||||
pthread_mutex_unlock(&log_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void log_flush()
|
||||
{
|
||||
if (mem_log) {
|
||||
pthread_mutex_lock(&log_mutex);
|
||||
if (getpid() == mem_log_owner)
|
||||
gzflush(mem_log, Z_SYNC_FLUSH);
|
||||
pthread_mutex_unlock(&log_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void malloc_profile_load_pointers()
|
||||
{
|
||||
realloc_original = dlsym(RTLD_NEXT, "realloc");
|
||||
malloc_original = dlsym(RTLD_NEXT, "malloc");
|
||||
calloc_original = dlsym(RTLD_NEXT, "calloc");
|
||||
free_original = dlsym(RTLD_NEXT, "free");
|
||||
int trace_mem = getenv("TRACE_MEM") != NULL;
|
||||
if (trace_mem)
|
||||
log_init();
|
||||
else
|
||||
paused = 1;
|
||||
}
|
||||
|
||||
void *calloc(size_t count, size_t size)
|
||||
{
|
||||
if (paused)
|
||||
return calloc_original ? calloc_original(count, size) : 0;
|
||||
paused = 1;
|
||||
if (!calloc_original) {
|
||||
malloc_profile_load_pointers();
|
||||
if (!calloc_original)
|
||||
return NULL;
|
||||
}
|
||||
void *result = calloc_original(count, size);
|
||||
if (mem_log) {
|
||||
Trace trace;
|
||||
get_backtrace(&trace);
|
||||
int created = trace_assign_id(&trace);
|
||||
if (created) {
|
||||
char *str = backtrace_to_string(&trace);
|
||||
if (str) {
|
||||
log_write(str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
char buffer[1024];
|
||||
sprintf(buffer, "[%f] t %lu : %p = c %lu %lu\n", get_unix_time(), trace.id, result, count, size);
|
||||
log_write(buffer);
|
||||
if (size_t_mul_overflow(count, size) || (count * size) > LARGE_THRESH)
|
||||
log_flush();
|
||||
}
|
||||
paused = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void *realloc(void *p, size_t size)
|
||||
{
|
||||
if (paused)
|
||||
return realloc_original ? realloc_original(p, size) : 0;
|
||||
paused = 1;
|
||||
if (!realloc_original) {
|
||||
malloc_profile_load_pointers();
|
||||
if (!realloc_original)
|
||||
return NULL;
|
||||
}
|
||||
void *result = realloc_original(p, size);
|
||||
if (mem_log) {
|
||||
Trace trace;
|
||||
get_backtrace(&trace);
|
||||
int created = trace_assign_id(&trace);
|
||||
if (created) {
|
||||
char *str = backtrace_to_string(&trace);
|
||||
if (str) {
|
||||
log_write(str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
char buffer[1024];
|
||||
sprintf(buffer, "[%f] t %lu : %p = r %p %lu\n", get_unix_time(), trace.id, result, p, size);
|
||||
log_write(buffer);
|
||||
if (size > LARGE_THRESH)
|
||||
log_flush();
|
||||
}
|
||||
paused = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void *malloc(size_t size)
|
||||
{
|
||||
if (paused)
|
||||
return malloc_original ? malloc_original(size) : 0;
|
||||
paused = 1;
|
||||
if (!malloc_original) {
|
||||
malloc_profile_load_pointers();
|
||||
if (!malloc_original)
|
||||
return NULL;
|
||||
}
|
||||
void *result = malloc_original(size);
|
||||
if (mem_log) {
|
||||
Trace trace;
|
||||
get_backtrace(&trace);
|
||||
int created = trace_assign_id(&trace);
|
||||
if (created) {
|
||||
char *str = backtrace_to_string(&trace);
|
||||
if (str) {
|
||||
log_write(str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
char buffer[1024];
|
||||
sprintf(buffer, "[%f] t %lu : %p = m %lu\n", get_unix_time(), trace.id, result, size);
|
||||
log_write(buffer);
|
||||
if (size > LARGE_THRESH)
|
||||
log_flush();
|
||||
}
|
||||
paused = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void free(void *p)
|
||||
{
|
||||
if (paused) {
|
||||
if (free_original)
|
||||
free_original(p);
|
||||
return;
|
||||
}
|
||||
paused = 1;
|
||||
if (!free_original) {
|
||||
malloc_profile_load_pointers();
|
||||
if (!free_original)
|
||||
return;
|
||||
}
|
||||
free_original(p);
|
||||
if (mem_log) {
|
||||
Trace trace;
|
||||
get_backtrace(&trace);
|
||||
int created = trace_assign_id(&trace);
|
||||
if (created) {
|
||||
char *str = backtrace_to_string(&trace);
|
||||
if (str) {
|
||||
log_write(str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
char buffer[1024];
|
||||
sprintf(buffer, "[%f] t %lu : 0 = f %p\n", get_unix_time(), trace.id, p);
|
||||
log_write(buffer);
|
||||
}
|
||||
paused = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
4
src/util/mem.h
Normal file
4
src/util/mem.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#ifndef MEM_H
|
||||
#define MEM_H
|
||||
|
||||
#endif
|
||||
103
src/util/print.c
103
src/util/print.c
@@ -1,83 +1,40 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "print.h"
|
||||
|
||||
int print_uchar(unsigned char v)
|
||||
Buffer *vbuffer_printf(Buffer *buffer, const char *fmt, va_list ap)
|
||||
{
|
||||
return printf("%u", v);
|
||||
va_list ap1;
|
||||
va_copy(ap1, ap);
|
||||
int ret = vsnprintf(NULL, 0, fmt, ap1) + 1;
|
||||
va_end(ap1);
|
||||
if (ret < 1)
|
||||
return buffer;
|
||||
|
||||
size_t size = (size_t)ret;
|
||||
if (!buffer)
|
||||
buffer = (Buffer*)calloc(1, sizeof(Buffer));
|
||||
if (!buffer->data) {
|
||||
buffer->size = 2 * size + 128;
|
||||
buffer->data = (char*) calloc(buffer->size, 1);
|
||||
} else if (strlen(buffer->data) + size >= buffer->size + 1) {
|
||||
buffer->size = 2 * (size + buffer->size) + 128;
|
||||
buffer->data = (char*) realloc(buffer->data, buffer->size);
|
||||
}
|
||||
|
||||
vsnprintf(buffer->data + strlen(buffer->data), size, fmt, ap);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int print_char(char v)
|
||||
Buffer *buffer_printf(Buffer *buffer, const char *fmt, ...)
|
||||
{
|
||||
return printf("%c", v);
|
||||
}
|
||||
va_list ap;
|
||||
|
||||
int print_short(short v)
|
||||
{
|
||||
return printf("%d", v);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
buffer = vbuffer_printf(buffer, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
int print_ushort(unsigned short v)
|
||||
{
|
||||
return printf("%u", v);
|
||||
}
|
||||
|
||||
int print_int(int v)
|
||||
{
|
||||
return printf("%d", v);
|
||||
}
|
||||
|
||||
int print_uint(unsigned v)
|
||||
{
|
||||
return printf("%u", v);
|
||||
}
|
||||
|
||||
int print_long(long v)
|
||||
{
|
||||
return printf("%ld", v);
|
||||
}
|
||||
|
||||
int print_ulong(unsigned long v)
|
||||
{
|
||||
return printf("%lu", v);
|
||||
}
|
||||
|
||||
int print_long_long(long long v)
|
||||
{
|
||||
return printf("%lld", v);
|
||||
}
|
||||
|
||||
int print_ulong_long(unsigned long long v)
|
||||
{
|
||||
return printf("%llu", v);
|
||||
}
|
||||
|
||||
int print_float(float v)
|
||||
{
|
||||
return printf("%f", (double)v);
|
||||
}
|
||||
|
||||
int print_double(double v)
|
||||
{
|
||||
return printf("%f", v);
|
||||
}
|
||||
|
||||
int print_long_double(long double v)
|
||||
{
|
||||
return printf("%Lf", v);
|
||||
}
|
||||
|
||||
int print_string(char *s)
|
||||
{
|
||||
return printf("%s", s);
|
||||
}
|
||||
|
||||
int print_pointer(void *v)
|
||||
{
|
||||
return printf("%p", v);
|
||||
}
|
||||
|
||||
int print_unknown()
|
||||
{
|
||||
return printf("(variable of unknown type)");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -1,61 +1,16 @@
|
||||
#ifndef PRINT_H
|
||||
#define PRINT_H
|
||||
|
||||
#ifdef HAS_GENERIC
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
int print_uchar(unsigned char v);
|
||||
typedef struct Buffer {
|
||||
char *data;
|
||||
size_t size;
|
||||
} Buffer;
|
||||
|
||||
int print_char(char v);
|
||||
|
||||
int print_short(short v);
|
||||
|
||||
int print_ushort(unsigned short v);
|
||||
|
||||
int print_int(int v);
|
||||
|
||||
int print_uint(unsigned v);
|
||||
|
||||
int print_long(long v);
|
||||
|
||||
int print_ulong(unsigned long v);
|
||||
|
||||
int print_long_long(long long v);
|
||||
|
||||
int print_ulong_long(unsigned long long v);
|
||||
|
||||
int print_float(float v);
|
||||
|
||||
int print_double(double v);
|
||||
|
||||
int print_long_double(long double v);
|
||||
|
||||
int print_string(char *s);
|
||||
|
||||
int print_pointer(void *v);
|
||||
|
||||
int print_unknown();
|
||||
|
||||
#define print(x) \
|
||||
_Generic((x), \
|
||||
unsigned char: print_uchar, \
|
||||
char: print_char, \
|
||||
short int: print_short, \
|
||||
unsigned short int: print_ushort, \
|
||||
int: print_int, \
|
||||
unsigned int: print_uint, \
|
||||
long int: print_long, \
|
||||
unsigned long int: print_ulong, \
|
||||
long long int: print_long_long, \
|
||||
unsigned long long int: print_ulong_long, \
|
||||
float: print_float, \
|
||||
double: print_double, \
|
||||
long double: print_long_double, \
|
||||
char *: print_string, \
|
||||
void *: print_pointer, \
|
||||
default : print_unknown)(x)
|
||||
|
||||
#else
|
||||
#define print(...) printf("Omitted, the compiler does not support C11 generics.\n")
|
||||
#endif
|
||||
Buffer *vbuffer_printf(Buffer *buffer, const char *fmt, va_list ap);
|
||||
Buffer *buffer_printf(Buffer *buffer, const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
|
||||
159
src/util/test.c
159
src/util/test.c
@@ -1,159 +0,0 @@
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "colors.h"
|
||||
#include "signals.h"
|
||||
#include "test.h"
|
||||
|
||||
typedef struct TestListItem {
|
||||
Test *test;
|
||||
const char *name;
|
||||
} TestListItem;
|
||||
|
||||
static GList *all_tests = NULL;
|
||||
|
||||
void register_test_(Test *test, const char *name)
|
||||
{
|
||||
TestListItem *item = (TestListItem *)calloc(sizeof(TestListItem), 1);
|
||||
item->test = test;
|
||||
item->name = name;
|
||||
all_tests = g_list_append(all_tests, item);
|
||||
}
|
||||
|
||||
static char *test_log_name_from_test_name(const char *test_name)
|
||||
{
|
||||
char *output_name = g_strdup_printf("test_%s.log", test_name);
|
||||
char *result = strdup(output_name);
|
||||
g_free(output_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void redirect_test_output(const char *test_name)
|
||||
{
|
||||
char *output_name = test_log_name_from_test_name(test_name);
|
||||
int fd = open(output_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
if (fd == -1)
|
||||
goto err;
|
||||
if (dup2(fd, STDOUT_FILENO) == -1)
|
||||
goto err;
|
||||
if (dup2(fd, STDERR_FILENO) == -1)
|
||||
goto err;
|
||||
|
||||
close(fd);
|
||||
free(output_name);
|
||||
return;
|
||||
err:
|
||||
fprintf(stderr, "tint2: Could not redirect test output to file name: %s\n", output_name);
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
free(output_name);
|
||||
}
|
||||
|
||||
__attribute__((noreturn))
|
||||
static void run_test_child(TestListItem *item)
|
||||
{
|
||||
reset_signals();
|
||||
redirect_test_output(item->name);
|
||||
bool result = true;
|
||||
item->test(&result);
|
||||
exit(result ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static FILE *open_test_log(const char *test_name)
|
||||
{
|
||||
char *output_name = test_log_name_from_test_name(test_name);
|
||||
FILE *log = fopen(output_name, "a");
|
||||
free(output_name);
|
||||
return log;
|
||||
}
|
||||
|
||||
static Status run_test_parent(TestListItem *item, pid_t child)
|
||||
{
|
||||
FILE *log = open_test_log(item->name);
|
||||
if (child == -1) {
|
||||
fprintf(log, "\n" "Test failed, fork failed\n");
|
||||
fclose(log);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
int child_status;
|
||||
pid_t ret_pid = waitpid(child, &child_status, 0);
|
||||
if (ret_pid != child) {
|
||||
fprintf(log, "\n" "Test failed, waitpid failed\n");
|
||||
fclose(log);
|
||||
return FAILURE;
|
||||
}
|
||||
if (WIFEXITED(child_status)) {
|
||||
int exit_status = WEXITSTATUS(child_status);
|
||||
if (exit_status == EXIT_SUCCESS) {
|
||||
fprintf(log, "\n" "Test succeeded.\n");
|
||||
fclose(log);
|
||||
return SUCCESS;
|
||||
} else {
|
||||
fprintf(log, "\n" "Test failed, exit status: %d.\n", exit_status);
|
||||
fclose(log);
|
||||
return FAILURE;
|
||||
}
|
||||
} else if (WIFSIGNALED(child_status)) {
|
||||
int signal = WTERMSIG(child_status);
|
||||
fprintf(log, "\n" "Test failed, child killed by signal: %d.\n", signal);
|
||||
fclose(log);
|
||||
return FAILURE;
|
||||
} else {
|
||||
fprintf(log, "\n" "Test failed, waitpid failed.\n");
|
||||
fclose(log);
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
static Status run_test(TestListItem *item)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
if (pid == 0)
|
||||
run_test_child(item);
|
||||
return run_test_parent(item, pid);
|
||||
}
|
||||
|
||||
void run_all_tests()
|
||||
{
|
||||
fprintf(stdout, BLUE "tint2: Running %d tests..." RESET "\n", g_list_length(all_tests));
|
||||
size_t count = 0, succeeded = 0, failed = 0;
|
||||
for (GList *l = all_tests; l; l = l->next) {
|
||||
TestListItem *item = (TestListItem *)l->data;
|
||||
Status status = run_test(item);
|
||||
count++;
|
||||
fprintf(stdout, BLUE "tint2: Test " YELLOW "%s" BLUE ": ", item->name);
|
||||
if (status == SUCCESS) {
|
||||
fprintf(stdout, GREEN "succeeded" RESET "\n");
|
||||
succeeded++;
|
||||
} else {
|
||||
fprintf(stdout, RED "failed" RESET "\n");
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
if (failed == 0)
|
||||
fprintf(stdout, BLUE "tint2: " GREEN "all %lu tests succeeded." RESET "\n", count);
|
||||
else
|
||||
fprintf(stdout, BLUE "tint2: " RED "%lu" BLUE " out of %lu tests " RED "failed." RESET "\n", failed, count);
|
||||
}
|
||||
|
||||
TEST(dummy) {
|
||||
int x = 2;
|
||||
int y = 2;
|
||||
ASSERT_EQUAL(x, y);
|
||||
}
|
||||
|
||||
TEST(dummyBad) {
|
||||
int x = 2;
|
||||
int y = 3;
|
||||
ASSERT_EQUAL(x, y);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
#ifndef TEST_H
|
||||
#define TEST_H
|
||||
|
||||
#include "bool.h"
|
||||
#include "print.h"
|
||||
|
||||
typedef void Test(Status *test_result_);
|
||||
|
||||
void register_test_(Test *test, const char *name);
|
||||
|
||||
#define TEST(name) \
|
||||
void test_##name(Status *test_result_); \
|
||||
__attribute__((constructor)) void test_register_##name() \
|
||||
{ \
|
||||
register_test_(test_##name, #name); \
|
||||
} \
|
||||
void test_##name(Status *test_result_)
|
||||
|
||||
void run_all_tests();
|
||||
|
||||
#define FAIL_TEST_ \
|
||||
*test_result_ = FAILURE; \
|
||||
return;
|
||||
|
||||
#define ASSERT(value) \
|
||||
if (!(value)) { \
|
||||
FAIL_TEST_ \
|
||||
}
|
||||
|
||||
#define ASSERT_EQUAL(a, b) \
|
||||
if (!(a == b)) { \
|
||||
printf("Assertion failed: %s == %s: ", #a, #b); \
|
||||
print(a); \
|
||||
printf(" != "); \
|
||||
print(b); \
|
||||
FAIL_TEST_ \
|
||||
}
|
||||
|
||||
#define ASSERT_DIFFERENT(a, b) \
|
||||
if (a == b) { \
|
||||
printf("Assertion failed: %s != %s: ", #a, #b); \
|
||||
print(a); \
|
||||
printf(" == "); \
|
||||
print(b); \
|
||||
FAIL_TEST_ \
|
||||
}
|
||||
|
||||
|
||||
#define ASSERT_STR_EQUAL(a, b) \
|
||||
if (strcmp(a, b) != 0) { \
|
||||
printf("Assertion failed: %s == %s: ", #a, #b); \
|
||||
print(a); \
|
||||
printf(" != "); \
|
||||
print(b); \
|
||||
FAIL_TEST_ \
|
||||
}
|
||||
|
||||
#define ASSERT_STR_DIFFERENT(a, b) \
|
||||
if (strcmp(a, b) == 0) { \
|
||||
printf("Assertion failed: %s != %s: ", #a, #b); \
|
||||
print(a); \
|
||||
printf(" == "); \
|
||||
print(b); \
|
||||
FAIL_TEST_ \
|
||||
}
|
||||
|
||||
#define ASSERT_TRUE(value) ASSERT_EQUAL(value, 1)
|
||||
#define ASSERT_FALSE(value) ASSERT_EQUAL(value, 0)
|
||||
#define ASSERT_NULL(value) ASSERT_EQUAL(value, NULL)
|
||||
#define ASSERT_NON_NULL(value) ASSERT_DIFFERENT(value, NULL)
|
||||
|
||||
#endif
|
||||
165
test/memtrace-analyze.py
Executable file
165
test/memtrace-analyze.py
Executable file
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python2
|
||||
|
||||
import gzip
|
||||
import sys
|
||||
|
||||
|
||||
def commas(v):
|
||||
return "{:12,}".format(v)
|
||||
|
||||
|
||||
class Event:
|
||||
def __init__(self):
|
||||
self.alloc = False
|
||||
self.free = False
|
||||
self.ts = None
|
||||
self.size = None
|
||||
self.addr = None
|
||||
self.trace_name = None
|
||||
|
||||
|
||||
class Analyzer:
|
||||
def __init__(self):
|
||||
self.traces = {}
|
||||
self.events = []
|
||||
self.all_allocations = {}
|
||||
self.new_allocations = {}
|
||||
self.all_mem = 0
|
||||
self.old_mem = 0
|
||||
|
||||
def read(self, path):
|
||||
with gzip.open(path, "rb") as f:
|
||||
try:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith("t"):
|
||||
self.add_trace(line)
|
||||
elif line.startswith("["):
|
||||
self.add_call(line)
|
||||
except:
|
||||
pass
|
||||
|
||||
def add_trace(self, line):
|
||||
t, name, content = line.split(" ", 2)
|
||||
name = int(name)
|
||||
assert(name > 0)
|
||||
assert(name not in self.traces)
|
||||
self.traces[name] = content.split("|")
|
||||
|
||||
def add_call(self, line):
|
||||
first, second = line.split(":", 1)
|
||||
first = first.strip()
|
||||
ts, t, tname = first.split(" ")
|
||||
ts = float(ts.replace("[", "").replace("]", ""))
|
||||
assert(t == "t")
|
||||
tname = int(tname)
|
||||
assert(tname in self.traces)
|
||||
second = second.strip()
|
||||
result, eq, func, params = second.split(" ", 3)
|
||||
assert(eq == "=")
|
||||
params = params.split(" ")
|
||||
e = Event()
|
||||
e.ts = ts
|
||||
e.trace_name = tname
|
||||
if func == "m" and result != "(nil)":
|
||||
e.alloc = True
|
||||
e.addr = int(result, 16)
|
||||
e.size = int(params[0])
|
||||
elif func == "c" and result != "(nil)":
|
||||
e.alloc = True
|
||||
e.addr = int(result, 16)
|
||||
e.size = int(params[0]) * int(params[1])
|
||||
elif func == "r":
|
||||
old_addr = params[0]
|
||||
size = int(params[1])
|
||||
if old_addr == "(nil)":
|
||||
if result != "(nil)":
|
||||
e.alloc = True
|
||||
e.addr = int(result, 16)
|
||||
e.size = size
|
||||
elif old_addr != "(nil)":
|
||||
if result != "(nil)":
|
||||
e.free = True
|
||||
e.addr = int(old_addr, 16)
|
||||
self.events.append(e)
|
||||
e = Event()
|
||||
e.ts = ts
|
||||
e.trace_name = tname
|
||||
e.alloc = True
|
||||
e.addr = int(result, 16)
|
||||
e.size = size
|
||||
elif func == "f":
|
||||
addr = params[0]
|
||||
if addr != "(nil)":
|
||||
e.free = True
|
||||
e.addr = int(addr, 16)
|
||||
if e.alloc or e.free:
|
||||
self.events.append(e)
|
||||
|
||||
|
||||
def analyze(self):
|
||||
self.all_allocations = {}
|
||||
self.new_allocations = {}
|
||||
self.all_mem = 0
|
||||
self.old_mem = 0
|
||||
self.peak_mem = 0
|
||||
period = 1
|
||||
next_ts_print = self.events[0].ts
|
||||
for e in self.events:
|
||||
if e.ts >= next_ts_print:
|
||||
self.print_allocations()
|
||||
next_ts_print += period
|
||||
self.process_event(e)
|
||||
self.print_allocations()
|
||||
self.print_leaks()
|
||||
|
||||
|
||||
def print_allocations(self):
|
||||
print "Memory usage: current:", commas(self.all_mem), " | delta:", commas(self.all_mem - self.old_mem), " | peak:", commas(self.peak_mem)
|
||||
self.old_mem = self.all_mem
|
||||
self.new_alllocations = {}
|
||||
|
||||
|
||||
def print_leaks(self):
|
||||
if not self.all_allocations:
|
||||
return
|
||||
print "Memory leaked:", commas(self.all_mem)
|
||||
leaks = [e for addr, e in self.all_allocations.iteritems()]
|
||||
leaks.sort(key=lambda e: e.size)
|
||||
for e in leaks:
|
||||
print "Leak:", commas(e.size)
|
||||
print "Allocated in:\n ", "\n ".join(self.traces[e.trace_name])
|
||||
|
||||
def process_event(self, e):
|
||||
if e.alloc:
|
||||
assert(e.addr not in self.all_allocations)
|
||||
self.all_allocations[e.addr] = e
|
||||
assert(e.addr not in self.new_allocations)
|
||||
self.new_allocations[e.addr] = e
|
||||
self.all_mem += e.size
|
||||
self.peak_mem = max(self.peak_mem, self.all_mem)
|
||||
else:
|
||||
assert(e.addr in self.all_allocations)
|
||||
e_alloc = self.all_allocations[e.addr]
|
||||
if e.ts - e_alloc.ts > 1. and e_alloc.size > 1e3 and False:
|
||||
print "Long-lived alloc:", commas(e_alloc.size), "freed after", commas(e.ts - e_alloc.ts) + "s"
|
||||
print "Allocated in:\n ", "\n ".join(self.traces[e_alloc.trace_name])
|
||||
print "Freed in:\n ", "\n ".join(self.traces[e.trace_name])
|
||||
if e_alloc.addr in self.new_allocations:
|
||||
if e_alloc.size > 1e7 and False:
|
||||
print "Large alloc:", commas(e_alloc.size), "freed after", commas(e.ts - e_alloc.ts) + "s"
|
||||
print "Allocated in:\n ", "\n ".join(self.traces[e_alloc.trace_name])
|
||||
print "Freed in:\n ", "\n ".join(self.traces[e.trace_name])
|
||||
del self.new_allocations[e_alloc.addr]
|
||||
del self.all_allocations[e_alloc.addr]
|
||||
self.all_mem -= e_alloc.size
|
||||
|
||||
|
||||
def main():
|
||||
a = Analyzer()
|
||||
a.read(sys.argv[1])
|
||||
a.analyze()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -285,29 +285,6 @@ def compile_and_report(src_dir, use_asan):
|
||||
print("Status: Succeeded in %.1f seconds" % (duration,), ok)
|
||||
|
||||
|
||||
def compile_remotely_and_report(host):
|
||||
print_err("Compiling on {0}...".format(host))
|
||||
print("# Compilation on {0}".format(host))
|
||||
start = time.time()
|
||||
c = run("ssh worker@{0} 'cd tint2 && git pull && mkdir -p build && rm -rf build && mkdir -p build && cd build && cmake .. && make && ./tint2 --version'".format(host), True)
|
||||
out, _ = c.communicate()
|
||||
duration = time.time() - start
|
||||
if c.returncode != 0:
|
||||
print("Status: Failed!", error)
|
||||
print("Output:")
|
||||
print("```\n" + out.strip() + "\n```")
|
||||
if "warning:" in out:
|
||||
print("Status: Succeeded with warnings!", warning)
|
||||
print("Warnings:")
|
||||
print("```", end="")
|
||||
for line in out.split("\n"):
|
||||
if "warning:" in line:
|
||||
print(line, end="")
|
||||
print("```", end="")
|
||||
else:
|
||||
print("Status: Succeeded in %.1f seconds" % (duration,), ok)
|
||||
|
||||
|
||||
def run_test(config, index, use_asan):
|
||||
print_err("Running test", index, "for config", config)
|
||||
print("# Test", index, "(ASAN on)" if use_asan else "")
|
||||
@@ -373,8 +350,6 @@ def main():
|
||||
show_timestamp()
|
||||
show_git_info(args.src_dir)
|
||||
show_system_info()
|
||||
compile_remotely_and_report("freebsd")
|
||||
compile_remotely_and_report("openbsd")
|
||||
for use_asan in [True, False]:
|
||||
compile_and_report(args.src_dir, use_asan)
|
||||
run_tests(use_asan)
|
||||
|
||||
@@ -6,4 +6,3 @@
|
||||
#define GETTEXT_PACKAGE
|
||||
#define HAVE_TRACING
|
||||
#define ENABLE_EXECINFO
|
||||
#define SN_API_NOT_YET_FROZEN
|
||||
|
||||
16
tint2.files
16
tint2.files
@@ -237,15 +237,9 @@ src/signals.c
|
||||
src/signals.h
|
||||
src/tracing.c
|
||||
src/tracing.h
|
||||
src/util/test.c
|
||||
src/util/test.h
|
||||
src/util/bool.h
|
||||
src/util/colors.h
|
||||
src/util/print.c
|
||||
src/util/mem.c
|
||||
src/util/mem.h
|
||||
src/util/addr2line.c
|
||||
src/util/addr2line.h
|
||||
src/util/print.h
|
||||
src/util/test.c
|
||||
src/util/test.h
|
||||
src/util/tracing.h
|
||||
src/util/tracing.c
|
||||
src/util/signals.h
|
||||
src/util/signals.c
|
||||
src/util/print.c
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
.
|
||||
./build
|
||||
./src
|
||||
./src/battery
|
||||
./src/clock
|
||||
./src/execplugin
|
||||
./src/launcher
|
||||
./src/sysmon
|
||||
./src/systray
|
||||
./src/taskbar
|
||||
./src/launcher
|
||||
./src/tint2conf
|
||||
./src/tooltip
|
||||
./src/util
|
||||
./src/execplugin
|
||||
./src/button
|
||||
./src/freespace
|
||||
./src/separator
|
||||
/usr/include/pango-1.0
|
||||
/usr/include/cairo
|
||||
/usr/include/glib-2.0
|
||||
/usr/lib/x86_64-linux-gnu/glib-2.0/include
|
||||
/usr/include/pixman-1
|
||||
/usr/include/freetype2
|
||||
/usr/include/libpng12
|
||||
/usr/include/librsvg-2.0
|
||||
/usr/include/gdk-pixbuf-2.0
|
||||
/usr/include/startup-notification-1.0
|
||||
/usr/include
|
||||
/usr/include/gtk-2.0
|
||||
/usr/include/glib-2.0
|
||||
/usr/include/gdk-pixbuf-2.0
|
||||
/usr/include/cairo
|
||||
/usr/include/pango-1.0
|
||||
/usr/include/startup-notification-1.0
|
||||
po
|
||||
src/tint2conf/po
|
||||
src/freespace
|
||||
|
||||
Reference in New Issue
Block a user