mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
perf script: Support standalone scripts and remove embedded scripting
Refactor 'perf script' to launch standalone scripts directly via fork() and execvp() and remove the legacy embedded Perl and Python scripting engines: - Remove the embedded Perl scripting engine (util/scripting-engines/trace-event-perl.c), Perl scripts, bin wrappers, and Trace-Util library (scripts/perl/Perf-Trace-Util/), and script_perl.sh test. - Remove libperl feature checks from Makefile.config, Makefile.perf, builtin-check.c, and Documentation/perf-check.txt. - Remove -g / --gen-script option and scripting_ops dispatch table from builtin-script.c and trace-event-scripting.c. - Hide the legacy -s / --script option and update script discovery in find_script() and list_available_scripts() to prioritize the system 'python' directory over bare filenames in the current directory. - Update the Python script shell tests (test_arm_coresight_disasm.sh, test_task_analyzer.sh, and test_*_python.sh) to invoke scripts via 'perf script <script>' rather than running the Python interpreter directly on the script file path. Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Ian Rogers <irogers@google.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
1b7e0280e2
commit
b02aa1d134
|
|
@ -121,7 +121,6 @@ FEATURE_TESTS_EXTRA := \
|
|||
libbfd-liberty \
|
||||
libbfd-liberty-z \
|
||||
libopencsd \
|
||||
libperl \
|
||||
llvm \
|
||||
libbpf \
|
||||
libpfm4 \
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ FILES= \
|
|||
test-libdebuginfod.bin \
|
||||
test-libnuma.bin \
|
||||
test-numa_num_possible_cpus.bin \
|
||||
test-libperl.bin \
|
||||
test-python-module.bin \
|
||||
test-libslang.bin \
|
||||
test-libtraceevent.bin \
|
||||
|
|
@ -242,25 +241,6 @@ $(OUTPUT)test-libtracefs.bin:
|
|||
$(OUTPUT)test-gtk4.bin:
|
||||
$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk4 2>/dev/null)
|
||||
|
||||
grep-libs = $(filter -l%,$(1))
|
||||
strip-libs = $(filter-out -l%,$(1))
|
||||
|
||||
PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null)
|
||||
PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS))
|
||||
PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS))
|
||||
PERL_EMBED_CCOPTS = $(shell perl -MExtUtils::Embed -e ccopts 2>/dev/null)
|
||||
FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
|
||||
|
||||
ifeq ($(CC_NO_CLANG), 0)
|
||||
PERL_EMBED_LDOPTS := $(filter-out -specs=%,$(PERL_EMBED_LDOPTS))
|
||||
PERL_EMBED_CCOPTS := $(filter-out -flto=auto -ffat-lto-objects, $(PERL_EMBED_CCOPTS))
|
||||
PERL_EMBED_CCOPTS := $(filter-out -specs=%,$(PERL_EMBED_CCOPTS))
|
||||
FLAGS_PERL_EMBED += -Wno-compound-token-split-by-macro
|
||||
endif
|
||||
|
||||
$(OUTPUT)test-libperl.bin:
|
||||
$(BUILD) $(FLAGS_PERL_EMBED)
|
||||
|
||||
$(OUTPUT)test-python-module.bin:
|
||||
$(BUILD) $(FLAGS_PYTHON_EMBED)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <EXTERN.h>
|
||||
#include <perl.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
perl_alloc();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -61,7 +61,6 @@ perf-util-y += arch/
|
|||
perf-y += arch/
|
||||
perf-test-y += arch/
|
||||
perf-ui-y += ui/
|
||||
perf-util-y += scripts/
|
||||
|
||||
gtk-y += ui/gtk/
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ feature::
|
|||
libLLVM / HAVE_LIBLLVM_SUPPORT
|
||||
libnuma / HAVE_LIBNUMA_SUPPORT
|
||||
libopencsd / HAVE_CSTRACE_SUPPORT
|
||||
libperl / HAVE_LIBPERL_SUPPORT
|
||||
libpfm4 / HAVE_LIBPFM
|
||||
libslang / HAVE_SLANG_SUPPORT
|
||||
libtraceevent / HAVE_LIBTRACEEVENT
|
||||
|
|
|
|||
|
|
@ -1296,7 +1296,6 @@ $(call detected_var,tipdir_SQ)
|
|||
$(call detected_var,srcdir_SQ)
|
||||
$(call detected_var,LIBDIR)
|
||||
$(call detected_var,GTK_CFLAGS)
|
||||
$(call detected_var,PERL_EMBED_CCOPTS)
|
||||
$(call detected_var,PYTHON_EMBED_CCOPTS)
|
||||
ifneq ($(BISON_FILE_PREFIX_MAP),)
|
||||
$(call detected_var,BISON_FILE_PREFIX_MAP)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ include ../scripts/utilities.mak
|
|||
#
|
||||
# Define CROSS_COMPILE as prefix name of compiler if you want cross-builds.
|
||||
#
|
||||
# Define LIBPERL to enable perl script extension.
|
||||
#
|
||||
# Define NO_PYTHON_MODULE to disable python script extension.
|
||||
#
|
||||
|
|
@ -483,11 +482,6 @@ OTHER_PROGRAMS = $(OUTPUT)perf
|
|||
ifndef SHELL_PATH
|
||||
SHELL_PATH = /bin/sh
|
||||
endif
|
||||
ifndef PERL_PATH
|
||||
PERL_PATH = /usr/bin/perl
|
||||
endif
|
||||
|
||||
export PERL_PATH
|
||||
|
||||
LIBPERF_BENCH_IN := $(OUTPUT)perf-bench-in.o
|
||||
LIBPERF_BENCH := $(OUTPUT)libperf-bench.a
|
||||
|
|
@ -892,14 +886,6 @@ endif
|
|||
$(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
$(call QUIET_INSTALL, perf-iostat) \
|
||||
$(INSTALL) $(OUTPUT)perf-iostat -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
ifdef LIBPERL
|
||||
$(call QUIET_INSTALL, perl-scripts) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
|
||||
$(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
|
||||
$(INSTALL) scripts/perl/*.pl -m 644 -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'; \
|
||||
$(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
|
||||
endif
|
||||
ifndef NO_PYTHON_MODULE
|
||||
$(call QUIET_INSTALL, python-scripts) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/python' && \
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ struct feature_status supported_features[] = {
|
|||
FEATURE_STATUS("libLLVM", HAVE_LIBLLVM_SUPPORT),
|
||||
FEATURE_STATUS("libnuma", HAVE_LIBNUMA_SUPPORT),
|
||||
FEATURE_STATUS("libopencsd", HAVE_CSTRACE_SUPPORT),
|
||||
FEATURE_STATUS_TIP("libperl", HAVE_LIBPERL_SUPPORT, "Deprecated, use LIBPERL=1 and install perl-ExtUtils-Embed/libperl-dev to build with it"),
|
||||
FEATURE_STATUS("python-module", HAVE_PYTHON_MODULE_SUPPORT),
|
||||
FEATURE_STATUS("libpfm4", HAVE_LIBPFM),
|
||||
FEATURE_STATUS("libslang", HAVE_SLANG_SUPPORT),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,3 +0,0 @@
|
|||
ifeq ($(CONFIG_LIBTRACEEVENT),y)
|
||||
perf-util-$(CONFIG_LIBPERL) += perl/Perf-Trace-Util/
|
||||
endif
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
perf-util-y += Context.o
|
||||
|
||||
CFLAGS_Context.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-bad-function-cast -Wno-declaration-after-statement -Wno-switch-enum
|
||||
CFLAGS_Context.o += -Wno-unused-parameter -Wno-nested-externs -Wno-undef
|
||||
CFLAGS_Context.o += -Wno-switch-default -Wno-shadow -Wno-thread-safety-analysis
|
||||
|
||||
ifeq ($(CC_NO_CLANG), 1)
|
||||
CFLAGS_Context.o += -Wno-unused-command-line-argument
|
||||
endif
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* This file was generated automatically by ExtUtils::ParseXS version 2.18_02 from the
|
||||
* contents of Context.xs. Do not edit this file, edit Context.xs instead.
|
||||
*
|
||||
* ANY CHANGES MADE HERE WILL BE LOST!
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#ifndef HAS_BOOL
|
||||
# define HAS_BOOL 1
|
||||
#endif
|
||||
#line 1 "Context.xs"
|
||||
/*
|
||||
* Context.xs. XS interfaces for perf script.
|
||||
*
|
||||
* Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
|
||||
*/
|
||||
|
||||
#include "EXTERN.h"
|
||||
#include "perl.h"
|
||||
#include "XSUB.h"
|
||||
#include "../../../util/trace-event.h"
|
||||
|
||||
#ifndef PERL_UNUSED_VAR
|
||||
# define PERL_UNUSED_VAR(var) if (0) var = var
|
||||
#endif
|
||||
|
||||
#line 42 "Context.c"
|
||||
|
||||
XS(XS_Perf__Trace__Context_common_pc); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Perf__Trace__Context_common_pc)
|
||||
{
|
||||
#ifdef dVAR
|
||||
dVAR; dXSARGS;
|
||||
#else
|
||||
dXSARGS;
|
||||
#endif
|
||||
if (items != 1)
|
||||
Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_pc", "context");
|
||||
PERL_UNUSED_VAR(cv); /* -W */
|
||||
{
|
||||
struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
|
||||
int RETVAL;
|
||||
dXSTARG;
|
||||
|
||||
RETVAL = common_pc(context);
|
||||
XSprePUSH; PUSHi((IV)RETVAL);
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
|
||||
XS(XS_Perf__Trace__Context_common_flags); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Perf__Trace__Context_common_flags)
|
||||
{
|
||||
#ifdef dVAR
|
||||
dVAR; dXSARGS;
|
||||
#else
|
||||
dXSARGS;
|
||||
#endif
|
||||
if (items != 1)
|
||||
Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_flags", "context");
|
||||
PERL_UNUSED_VAR(cv); /* -W */
|
||||
{
|
||||
struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
|
||||
int RETVAL;
|
||||
dXSTARG;
|
||||
|
||||
RETVAL = common_flags(context);
|
||||
XSprePUSH; PUSHi((IV)RETVAL);
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
|
||||
XS(XS_Perf__Trace__Context_common_lock_depth); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Perf__Trace__Context_common_lock_depth)
|
||||
{
|
||||
#ifdef dVAR
|
||||
dVAR; dXSARGS;
|
||||
#else
|
||||
dXSARGS;
|
||||
#endif
|
||||
if (items != 1)
|
||||
Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_lock_depth", "context");
|
||||
PERL_UNUSED_VAR(cv); /* -W */
|
||||
{
|
||||
struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0)));
|
||||
int RETVAL;
|
||||
dXSTARG;
|
||||
|
||||
RETVAL = common_lock_depth(context);
|
||||
XSprePUSH; PUSHi((IV)RETVAL);
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
XS(boot_Perf__Trace__Context); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(boot_Perf__Trace__Context)
|
||||
{
|
||||
#ifdef dVAR
|
||||
dVAR; dXSARGS;
|
||||
#else
|
||||
dXSARGS;
|
||||
#endif
|
||||
const char* file = __FILE__;
|
||||
|
||||
PERL_UNUSED_VAR(cv); /* -W */
|
||||
PERL_UNUSED_VAR(items); /* -W */
|
||||
XS_VERSION_BOOTCHECK ;
|
||||
|
||||
newXSproto("Perf::Trace::Context::common_pc", XS_Perf__Trace__Context_common_pc, file, "$");
|
||||
newXSproto("Perf::Trace::Context::common_flags", XS_Perf__Trace__Context_common_flags, file, "$");
|
||||
newXSproto("Perf::Trace::Context::common_lock_depth", XS_Perf__Trace__Context_common_lock_depth, file, "$");
|
||||
if (PL_unitcheckav)
|
||||
call_list(PL_scopestack_ix, PL_unitcheckav);
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Context.xs. XS interfaces for perf script.
|
||||
*
|
||||
* Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.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 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 "EXTERN.h"
|
||||
#include "perl.h"
|
||||
#include "XSUB.h"
|
||||
#include "../../../perf.h"
|
||||
#include "../../../util/trace-event.h"
|
||||
|
||||
MODULE = Perf::Trace::Context PACKAGE = Perf::Trace::Context
|
||||
PROTOTYPES: ENABLE
|
||||
|
||||
int
|
||||
common_pc(context)
|
||||
struct scripting_context * context
|
||||
|
||||
int
|
||||
common_flags(context)
|
||||
struct scripting_context * context
|
||||
|
||||
int
|
||||
common_lock_depth(context)
|
||||
struct scripting_context * context
|
||||
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
use 5.010000;
|
||||
use ExtUtils::MakeMaker;
|
||||
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
|
||||
# the contents of the Makefile that is written.
|
||||
WriteMakefile(
|
||||
NAME => 'Perf::Trace::Context',
|
||||
VERSION_FROM => 'lib/Perf/Trace/Context.pm', # finds $VERSION
|
||||
PREREQ_PM => {}, # e.g., Module::Name => 1.1
|
||||
($] >= 5.005 ? ## Add these new keywords supported since 5.005
|
||||
(ABSTRACT_FROM => 'lib/Perf/Trace/Context.pm', # retrieve abstract from module
|
||||
AUTHOR => 'Tom Zanussi <tzanussi@gmail.com>') : ()),
|
||||
LIBS => [''], # e.g., '-lm'
|
||||
DEFINE => '-I ../..', # e.g., '-DHAVE_SOMETHING'
|
||||
INC => '-I.', # e.g., '-I. -I/usr/include/other'
|
||||
# Un-comment this if you add C files to link with later:
|
||||
OBJECT => 'Context.o', # link all the C files too
|
||||
);
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
Perf-Trace-Util version 0.01
|
||||
============================
|
||||
|
||||
This module contains utility functions for use with perf script.
|
||||
|
||||
Core.pm and Util.pm are pure Perl modules; Core.pm contains routines
|
||||
that the core perf support for Perl calls on and should always be
|
||||
'used', while Util.pm contains useful but optional utility functions
|
||||
that scripts may want to use. Context.pm contains the Perl->C
|
||||
interface that allows scripts to access data in the embedding perf
|
||||
executable; scripts wishing to do that should 'use Context.pm'.
|
||||
|
||||
The Perl->C perf interface is completely driven by Context.xs. If you
|
||||
want to add new Perl functions that end up accessing C data in the
|
||||
perf executable, you add desciptions of the new functions here.
|
||||
scripting_context is a pointer to the perf data in the perf executable
|
||||
that you want to access - it's passed as the second parameter,
|
||||
$context, to all handler functions.
|
||||
|
||||
After you do that:
|
||||
|
||||
perl Makefile.PL # to create a Makefile for the next step
|
||||
make # to create Context.c
|
||||
|
||||
edit Context.c to add const to the char* file = __FILE__ line in
|
||||
XS(boot_Perf__Trace__Context) to silence a warning/error.
|
||||
|
||||
You can delete the Makefile, object files and anything else that was
|
||||
generated e.g. blib and shared library, etc, except for of course
|
||||
Context.c
|
||||
|
||||
You should then be able to run the normal perf make as usual.
|
||||
|
||||
INSTALLATION
|
||||
|
||||
Building perf with perf script Perl scripting should install this
|
||||
module in the right place.
|
||||
|
||||
You should make sure libperl and ExtUtils/Embed.pm are installed first
|
||||
e.g. apt-get install libperl-dev or yum install perl-ExtUtils-Embed.
|
||||
|
||||
DEPENDENCIES
|
||||
|
||||
This module requires these other modules and libraries:
|
||||
|
||||
None
|
||||
|
||||
COPYRIGHT AND LICENCE
|
||||
|
||||
Copyright (C) 2009 by Tom Zanussi <tzanussi@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself, either Perl version 5.10.0 or,
|
||||
at your option, any later version of Perl 5 you may have available.
|
||||
|
||||
Alternatively, this software may be distributed under the terms of the
|
||||
GNU General Public License ("GPL") version 2 as published by the Free
|
||||
Software Foundation.
|
||||
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
package Perf::Trace::Context;
|
||||
|
||||
use 5.010000;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require Exporter;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
|
||||
our %EXPORT_TAGS = ( 'all' => [ qw(
|
||||
) ] );
|
||||
|
||||
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
||||
|
||||
our @EXPORT = qw(
|
||||
common_pc common_flags common_lock_depth
|
||||
);
|
||||
|
||||
our $VERSION = '0.01';
|
||||
|
||||
require XSLoader;
|
||||
XSLoader::load('Perf::Trace::Context', $VERSION);
|
||||
|
||||
1;
|
||||
__END__
|
||||
=head1 NAME
|
||||
|
||||
Perf::Trace::Context - Perl extension for accessing functions in perf.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Perf::Trace::Context;
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Perf (script) documentation
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) 2009 by Tom Zanussi
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself, either Perl version 5.10.0 or,
|
||||
at your option, any later version of Perl 5 you may have available.
|
||||
|
||||
Alternatively, this software may be distributed under the terms of the
|
||||
GNU General Public License ("GPL") version 2 as published by the Free
|
||||
Software Foundation.
|
||||
|
||||
=cut
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
package Perf::Trace::Core;
|
||||
|
||||
use 5.010000;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require Exporter;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
|
||||
our %EXPORT_TAGS = ( 'all' => [ qw(
|
||||
) ] );
|
||||
|
||||
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
||||
|
||||
our @EXPORT = qw(
|
||||
define_flag_field define_flag_value flag_str dump_flag_fields
|
||||
define_symbolic_field define_symbolic_value symbol_str dump_symbolic_fields
|
||||
trace_flag_str
|
||||
);
|
||||
|
||||
our $VERSION = '0.01';
|
||||
|
||||
my %trace_flags = (0x00 => "NONE",
|
||||
0x01 => "IRQS_OFF",
|
||||
0x02 => "IRQS_NOSUPPORT",
|
||||
0x04 => "NEED_RESCHED",
|
||||
0x08 => "HARDIRQ",
|
||||
0x10 => "SOFTIRQ");
|
||||
|
||||
sub trace_flag_str
|
||||
{
|
||||
my ($value) = @_;
|
||||
|
||||
my $string;
|
||||
|
||||
my $print_delim = 0;
|
||||
|
||||
foreach my $idx (sort {$a <=> $b} keys %trace_flags) {
|
||||
if (!$value && !$idx) {
|
||||
$string .= "NONE";
|
||||
last;
|
||||
}
|
||||
|
||||
if ($idx && ($value & $idx) == $idx) {
|
||||
if ($print_delim) {
|
||||
$string .= " | ";
|
||||
}
|
||||
$string .= "$trace_flags{$idx}";
|
||||
$print_delim = 1;
|
||||
$value &= ~$idx;
|
||||
}
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
my %flag_fields;
|
||||
my %symbolic_fields;
|
||||
|
||||
sub flag_str
|
||||
{
|
||||
my ($event_name, $field_name, $value) = @_;
|
||||
|
||||
my $string;
|
||||
|
||||
if ($flag_fields{$event_name}{$field_name}) {
|
||||
my $print_delim = 0;
|
||||
foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event_name}{$field_name}{"values"}}) {
|
||||
if (!$value && !$idx) {
|
||||
$string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}";
|
||||
last;
|
||||
}
|
||||
if ($idx && ($value & $idx) == $idx) {
|
||||
if ($print_delim && $flag_fields{$event_name}{$field_name}{'delim'}) {
|
||||
$string .= " $flag_fields{$event_name}{$field_name}{'delim'} ";
|
||||
}
|
||||
$string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}";
|
||||
$print_delim = 1;
|
||||
$value &= ~$idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
sub define_flag_field
|
||||
{
|
||||
my ($event_name, $field_name, $delim) = @_;
|
||||
|
||||
$flag_fields{$event_name}{$field_name}{"delim"} = $delim;
|
||||
}
|
||||
|
||||
sub define_flag_value
|
||||
{
|
||||
my ($event_name, $field_name, $value, $field_str) = @_;
|
||||
|
||||
$flag_fields{$event_name}{$field_name}{"values"}{$value} = $field_str;
|
||||
}
|
||||
|
||||
sub dump_flag_fields
|
||||
{
|
||||
for my $event (keys %flag_fields) {
|
||||
print "event $event:\n";
|
||||
for my $field (keys %{$flag_fields{$event}}) {
|
||||
print " field: $field:\n";
|
||||
print " delim: $flag_fields{$event}{$field}{'delim'}\n";
|
||||
foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event}{$field}{"values"}}) {
|
||||
print " value $idx: $flag_fields{$event}{$field}{'values'}{$idx}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub symbol_str
|
||||
{
|
||||
my ($event_name, $field_name, $value) = @_;
|
||||
|
||||
if ($symbolic_fields{$event_name}{$field_name}) {
|
||||
foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event_name}{$field_name}{"values"}}) {
|
||||
if (!$value && !$idx) {
|
||||
return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}";
|
||||
last;
|
||||
}
|
||||
if ($value == $idx) {
|
||||
return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub define_symbolic_field
|
||||
{
|
||||
my ($event_name, $field_name) = @_;
|
||||
|
||||
# nothing to do, really
|
||||
}
|
||||
|
||||
sub define_symbolic_value
|
||||
{
|
||||
my ($event_name, $field_name, $value, $field_str) = @_;
|
||||
|
||||
$symbolic_fields{$event_name}{$field_name}{"values"}{$value} = $field_str;
|
||||
}
|
||||
|
||||
sub dump_symbolic_fields
|
||||
{
|
||||
for my $event (keys %symbolic_fields) {
|
||||
print "event $event:\n";
|
||||
for my $field (keys %{$symbolic_fields{$event}}) {
|
||||
print " field: $field:\n";
|
||||
foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event}{$field}{"values"}}) {
|
||||
print " value $idx: $symbolic_fields{$event}{$field}{'values'}{$idx}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
=head1 NAME
|
||||
|
||||
Perf::Trace::Core - Perl extension for perf script
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Perf::Trace::Core
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Perf (script) documentation
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) 2009 by Tom Zanussi
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself, either Perl version 5.10.0 or,
|
||||
at your option, any later version of Perl 5 you may have available.
|
||||
|
||||
Alternatively, this software may be distributed under the terms of the
|
||||
GNU General Public License ("GPL") version 2 as published by the Free
|
||||
Software Foundation.
|
||||
|
||||
=cut
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
package Perf::Trace::Util;
|
||||
|
||||
use 5.010000;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require Exporter;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
|
||||
our %EXPORT_TAGS = ( 'all' => [ qw(
|
||||
) ] );
|
||||
|
||||
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
||||
|
||||
our @EXPORT = qw(
|
||||
avg nsecs nsecs_secs nsecs_nsecs nsecs_usecs print_nsecs
|
||||
clear_term
|
||||
);
|
||||
|
||||
our $VERSION = '0.01';
|
||||
|
||||
sub avg
|
||||
{
|
||||
my ($total, $n) = @_;
|
||||
|
||||
return $total / $n;
|
||||
}
|
||||
|
||||
my $NSECS_PER_SEC = 1000000000;
|
||||
|
||||
sub nsecs
|
||||
{
|
||||
my ($secs, $nsecs) = @_;
|
||||
|
||||
return $secs * $NSECS_PER_SEC + $nsecs;
|
||||
}
|
||||
|
||||
sub nsecs_secs {
|
||||
my ($nsecs) = @_;
|
||||
|
||||
return $nsecs / $NSECS_PER_SEC;
|
||||
}
|
||||
|
||||
sub nsecs_nsecs {
|
||||
my ($nsecs) = @_;
|
||||
|
||||
return $nsecs % $NSECS_PER_SEC;
|
||||
}
|
||||
|
||||
sub nsecs_str {
|
||||
my ($nsecs) = @_;
|
||||
|
||||
my $str = sprintf("%5u.%09u", nsecs_secs($nsecs), nsecs_nsecs($nsecs));
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
sub clear_term
|
||||
{
|
||||
print "\x1b[H\x1b[2J";
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
=head1 NAME
|
||||
|
||||
Perf::Trace::Util - Perl extension for perf script
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Perf::Trace::Util;
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Perf (script) documentation
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Tom Zanussi, E<lt>tzanussi@gmail.com<gt>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) 2009 by Tom Zanussi
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself, either Perl version 5.10.0 or,
|
||||
at your option, any later version of Perl 5 you may have available.
|
||||
|
||||
Alternatively, this software may be distributed under the terms of the
|
||||
GNU General Public License ("GPL") version 2 as published by the Free
|
||||
Software Foundation.
|
||||
|
||||
=cut
|
||||
|
|
@ -1 +0,0 @@
|
|||
struct scripting_context * T_PTR
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
perf record -a -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
(perf record -e raw_syscalls:sys_exit $@ || \
|
||||
perf record -e syscalls:sys_exit $@) 2> /dev/null
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide failed syscalls
|
||||
# args: [comm]
|
||||
if [ $# -gt 0 ] ; then
|
||||
if ! expr match "$1" "-" > /dev/null ; then
|
||||
comm=$1
|
||||
shift
|
||||
fi
|
||||
fi
|
||||
perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/failed-syscalls.pl $comm
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
perf record -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/bash
|
||||
# description: r/w activity for a program, by file
|
||||
# args: <comm>
|
||||
if [ $# -lt 1 ] ; then
|
||||
echo "usage: rw-by-file <comm>"
|
||||
exit
|
||||
fi
|
||||
comm=$1
|
||||
shift
|
||||
perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-file.pl $comm
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide r/w activity
|
||||
perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-pid.pl
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide r/w top
|
||||
# args: [interval]
|
||||
n_args=0
|
||||
for i in "$@"
|
||||
do
|
||||
if expr match "$i" "-" > /dev/null ; then
|
||||
break
|
||||
fi
|
||||
n_args=$(( $n_args + 1 ))
|
||||
done
|
||||
if [ "$n_args" -gt 1 ] ; then
|
||||
echo "usage: rwtop-report [interval]"
|
||||
exit
|
||||
fi
|
||||
if [ "$n_args" -gt 0 ] ; then
|
||||
interval=$1
|
||||
shift
|
||||
fi
|
||||
perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rwtop.pl $interval
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/bash
|
||||
perf record -e sched:sched_switch -e sched:sched_wakeup $@
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide min/max/avg wakeup latency
|
||||
perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/wakeup-latency.pl
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
# perf script event handlers, generated by perf script -g perl
|
||||
# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
|
||||
# Licensed under the terms of the GNU GPL License version 2
|
||||
|
||||
# This script tests basic functionality such as flag and symbol
|
||||
# strings, common_xxx() calls back into perf, begin, end, unhandled
|
||||
# events, etc. Basically, if this script runs successfully and
|
||||
# displays expected results, perl scripting support should be ok.
|
||||
|
||||
use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
||||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Context;
|
||||
use Perf::Trace::Util;
|
||||
|
||||
sub trace_begin
|
||||
{
|
||||
print "trace_begin\n";
|
||||
}
|
||||
|
||||
sub trace_end
|
||||
{
|
||||
print "trace_end\n";
|
||||
|
||||
print_unhandled();
|
||||
}
|
||||
|
||||
sub irq::softirq_entry
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$vec) = @_;
|
||||
|
||||
print_header($event_name, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm);
|
||||
|
||||
print_uncommon($context);
|
||||
|
||||
printf("vec=%s\n",
|
||||
symbol_str("irq::softirq_entry", "vec", $vec));
|
||||
}
|
||||
|
||||
sub kmem::kmalloc
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$call_site, $ptr, $bytes_req, $bytes_alloc,
|
||||
$gfp_flags) = @_;
|
||||
|
||||
print_header($event_name, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm);
|
||||
|
||||
print_uncommon($context);
|
||||
|
||||
printf("call_site=%p, ptr=%p, bytes_req=%u, bytes_alloc=%u, ".
|
||||
"gfp_flags=%s\n",
|
||||
$call_site, $ptr, $bytes_req, $bytes_alloc,
|
||||
|
||||
flag_str("kmem::kmalloc", "gfp_flags", $gfp_flags));
|
||||
}
|
||||
|
||||
# print trace fields not included in handler args
|
||||
sub print_uncommon
|
||||
{
|
||||
my ($context) = @_;
|
||||
|
||||
printf("common_preempt_count=%d, common_flags=%s, common_lock_depth=%d, ",
|
||||
common_pc($context), trace_flag_str(common_flags($context)),
|
||||
common_lock_depth($context));
|
||||
|
||||
}
|
||||
|
||||
my %unhandled;
|
||||
|
||||
sub print_unhandled
|
||||
{
|
||||
if ((scalar keys %unhandled) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
print "\nunhandled events:\n\n";
|
||||
|
||||
printf("%-40s %10s\n", "event", "count");
|
||||
printf("%-40s %10s\n", "----------------------------------------",
|
||||
"-----------");
|
||||
|
||||
foreach my $event_name (keys %unhandled) {
|
||||
printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
|
||||
}
|
||||
}
|
||||
|
||||
sub trace_unhandled
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain) = @_;
|
||||
|
||||
$unhandled{$event_name}++;
|
||||
}
|
||||
|
||||
sub print_header
|
||||
{
|
||||
my ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;
|
||||
|
||||
printf("%-20s %5u %05u.%09u %8u %-20s ",
|
||||
$event_name, $cpu, $secs, $nsecs, $pid, $comm);
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
# failed system call counts
|
||||
# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
|
||||
# Licensed under the terms of the GNU GPL License version 2
|
||||
#
|
||||
# Displays system-wide failed system call totals
|
||||
# If a [comm] arg is specified, only syscalls called by [comm] are displayed.
|
||||
|
||||
use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
||||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Context;
|
||||
use Perf::Trace::Util;
|
||||
|
||||
my $for_comm = shift;
|
||||
|
||||
my %failed_syscalls;
|
||||
|
||||
sub raw_syscalls::sys_exit
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$id, $ret) = @_;
|
||||
|
||||
if ($ret < 0) {
|
||||
$failed_syscalls{$common_comm}++;
|
||||
}
|
||||
}
|
||||
|
||||
sub syscalls::sys_exit
|
||||
{
|
||||
raw_syscalls::sys_exit(@_)
|
||||
}
|
||||
|
||||
sub trace_end
|
||||
{
|
||||
printf("\nfailed syscalls by comm:\n\n");
|
||||
|
||||
printf("%-20s %10s\n", "comm", "# errors");
|
||||
printf("%-20s %6s %10s\n", "--------------------", "----------");
|
||||
|
||||
foreach my $comm (sort {$failed_syscalls{$b} <=> $failed_syscalls{$a}}
|
||||
keys %failed_syscalls) {
|
||||
next if ($for_comm && $comm ne $for_comm);
|
||||
|
||||
printf("%-20s %10s\n", $comm, $failed_syscalls{$comm});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
#!/usr/bin/perl -w
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
|
||||
|
||||
# Display r/w activity for files read/written to for a given program
|
||||
|
||||
# The common_* event handler fields are the most useful fields common to
|
||||
# all events. They don't necessarily correspond to the 'common_*' fields
|
||||
# in the status files. Those fields not available as handler params can
|
||||
# be retrieved via script functions of the form get_common_*().
|
||||
|
||||
use 5.010000;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
||||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Util;
|
||||
|
||||
my $usage = "perf script -s rw-by-file.pl <comm>\n";
|
||||
|
||||
my $for_comm = shift or die $usage;
|
||||
|
||||
my %reads;
|
||||
my %writes;
|
||||
|
||||
sub syscalls::sys_enter_read
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain, $nr, $fd, $buf, $count) = @_;
|
||||
|
||||
if ($common_comm eq $for_comm) {
|
||||
$reads{$fd}{bytes_requested} += $count;
|
||||
$reads{$fd}{total_reads}++;
|
||||
}
|
||||
}
|
||||
|
||||
sub syscalls::sys_enter_write
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain, $nr, $fd, $buf, $count) = @_;
|
||||
|
||||
if ($common_comm eq $for_comm) {
|
||||
$writes{$fd}{bytes_written} += $count;
|
||||
$writes{$fd}{total_writes}++;
|
||||
}
|
||||
}
|
||||
|
||||
sub trace_end
|
||||
{
|
||||
printf("file read counts for $for_comm:\n\n");
|
||||
|
||||
printf("%6s %10s %10s\n", "fd", "# reads", "bytes_requested");
|
||||
printf("%6s %10s %10s\n", "------", "----------", "-----------");
|
||||
|
||||
foreach my $fd (sort {$reads{$b}{bytes_requested} <=>
|
||||
$reads{$a}{bytes_requested}} keys %reads) {
|
||||
my $total_reads = $reads{$fd}{total_reads};
|
||||
my $bytes_requested = $reads{$fd}{bytes_requested};
|
||||
printf("%6u %10u %10u\n", $fd, $total_reads, $bytes_requested);
|
||||
}
|
||||
|
||||
printf("\nfile write counts for $for_comm:\n\n");
|
||||
|
||||
printf("%6s %10s %10s\n", "fd", "# writes", "bytes_written");
|
||||
printf("%6s %10s %10s\n", "------", "----------", "-----------");
|
||||
|
||||
foreach my $fd (sort {$writes{$b}{bytes_written} <=>
|
||||
$writes{$a}{bytes_written}} keys %writes) {
|
||||
my $total_writes = $writes{$fd}{total_writes};
|
||||
my $bytes_written = $writes{$fd}{bytes_written};
|
||||
printf("%6u %10u %10u\n", $fd, $total_writes, $bytes_written);
|
||||
}
|
||||
|
||||
print_unhandled();
|
||||
}
|
||||
|
||||
my %unhandled;
|
||||
|
||||
sub print_unhandled
|
||||
{
|
||||
if ((scalar keys %unhandled) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
print "\nunhandled events:\n\n";
|
||||
|
||||
printf("%-40s %10s\n", "event", "count");
|
||||
printf("%-40s %10s\n", "----------------------------------------",
|
||||
"-----------");
|
||||
|
||||
foreach my $event_name (keys %unhandled) {
|
||||
printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
|
||||
}
|
||||
}
|
||||
|
||||
sub trace_unhandled
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain) = @_;
|
||||
|
||||
$unhandled{$event_name}++;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
#!/usr/bin/perl -w
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
|
||||
|
||||
# Display r/w activity for all processes
|
||||
|
||||
# The common_* event handler fields are the most useful fields common to
|
||||
# all events. They don't necessarily correspond to the 'common_*' fields
|
||||
# in the status files. Those fields not available as handler params can
|
||||
# be retrieved via script functions of the form get_common_*().
|
||||
|
||||
use 5.010000;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
||||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Util;
|
||||
|
||||
my %reads;
|
||||
my %writes;
|
||||
|
||||
sub syscalls::sys_exit_read
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$nr, $ret) = @_;
|
||||
|
||||
if ($ret > 0) {
|
||||
$reads{$common_pid}{bytes_read} += $ret;
|
||||
} else {
|
||||
if (!defined ($reads{$common_pid}{bytes_read})) {
|
||||
$reads{$common_pid}{bytes_read} = 0;
|
||||
}
|
||||
$reads{$common_pid}{errors}{$ret}++;
|
||||
}
|
||||
}
|
||||
|
||||
sub syscalls::sys_enter_read
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$nr, $fd, $buf, $count) = @_;
|
||||
|
||||
$reads{$common_pid}{bytes_requested} += $count;
|
||||
$reads{$common_pid}{total_reads}++;
|
||||
$reads{$common_pid}{comm} = $common_comm;
|
||||
}
|
||||
|
||||
sub syscalls::sys_exit_write
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$nr, $ret) = @_;
|
||||
|
||||
if ($ret <= 0) {
|
||||
$writes{$common_pid}{errors}{$ret}++;
|
||||
}
|
||||
}
|
||||
|
||||
sub syscalls::sys_enter_write
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$nr, $fd, $buf, $count) = @_;
|
||||
|
||||
$writes{$common_pid}{bytes_written} += $count;
|
||||
$writes{$common_pid}{total_writes}++;
|
||||
$writes{$common_pid}{comm} = $common_comm;
|
||||
}
|
||||
|
||||
sub trace_end
|
||||
{
|
||||
printf("read counts by pid:\n\n");
|
||||
|
||||
printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
|
||||
"# reads", "bytes_requested", "bytes_read");
|
||||
printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------",
|
||||
"-----------", "----------", "----------");
|
||||
|
||||
foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=>
|
||||
($reads{$a}{bytes_read} || 0) } keys %reads) {
|
||||
my $comm = $reads{$pid}{comm} || "";
|
||||
my $total_reads = $reads{$pid}{total_reads} || 0;
|
||||
my $bytes_requested = $reads{$pid}{bytes_requested} || 0;
|
||||
my $bytes_read = $reads{$pid}{bytes_read} || 0;
|
||||
|
||||
printf("%6s %-20s %10s %10s %10s\n", $pid, $comm,
|
||||
$total_reads, $bytes_requested, $bytes_read);
|
||||
}
|
||||
|
||||
printf("\nfailed reads by pid:\n\n");
|
||||
|
||||
printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors");
|
||||
printf("%6s %20s %6s %10s\n", "------", "--------------------",
|
||||
"------", "----------");
|
||||
|
||||
my @errcounts = ();
|
||||
|
||||
foreach my $pid (keys %reads) {
|
||||
foreach my $error (keys %{$reads{$pid}{errors}}) {
|
||||
my $comm = $reads{$pid}{comm} || "";
|
||||
my $errcount = $reads{$pid}{errors}{$error} || 0;
|
||||
push @errcounts, [$pid, $comm, $error, $errcount];
|
||||
}
|
||||
}
|
||||
|
||||
@errcounts = sort { $b->[3] <=> $a->[3] } @errcounts;
|
||||
|
||||
for my $i (0 .. $#errcounts) {
|
||||
printf("%6d %-20s %6d %10s\n", $errcounts[$i][0],
|
||||
$errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]);
|
||||
}
|
||||
|
||||
printf("\nwrite counts by pid:\n\n");
|
||||
|
||||
printf("%6s %20s %10s %10s\n", "pid", "comm",
|
||||
"# writes", "bytes_written");
|
||||
printf("%6s %-20s %10s %10s\n", "------", "--------------------",
|
||||
"-----------", "----------");
|
||||
|
||||
foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=>
|
||||
($writes{$a}{bytes_written} || 0)} keys %writes) {
|
||||
my $comm = $writes{$pid}{comm} || "";
|
||||
my $total_writes = $writes{$pid}{total_writes} || 0;
|
||||
my $bytes_written = $writes{$pid}{bytes_written} || 0;
|
||||
|
||||
printf("%6s %-20s %10s %10s\n", $pid, $comm,
|
||||
$total_writes, $bytes_written);
|
||||
}
|
||||
|
||||
printf("\nfailed writes by pid:\n\n");
|
||||
|
||||
printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors");
|
||||
printf("%6s %20s %6s %10s\n", "------", "--------------------",
|
||||
"------", "----------");
|
||||
|
||||
@errcounts = ();
|
||||
|
||||
foreach my $pid (keys %writes) {
|
||||
foreach my $error (keys %{$writes{$pid}{errors}}) {
|
||||
my $comm = $writes{$pid}{comm} || "";
|
||||
my $errcount = $writes{$pid}{errors}{$error} || 0;
|
||||
push @errcounts, [$pid, $comm, $error, $errcount];
|
||||
}
|
||||
}
|
||||
|
||||
@errcounts = sort { $b->[3] <=> $a->[3] } @errcounts;
|
||||
|
||||
for my $i (0 .. $#errcounts) {
|
||||
printf("%6d %-20s %6d %10s\n", $errcounts[$i][0],
|
||||
$errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]);
|
||||
}
|
||||
|
||||
print_unhandled();
|
||||
}
|
||||
|
||||
my %unhandled;
|
||||
|
||||
sub print_unhandled
|
||||
{
|
||||
if ((scalar keys %unhandled) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
print "\nunhandled events:\n\n";
|
||||
|
||||
printf("%-40s %10s\n", "event", "count");
|
||||
printf("%-40s %10s\n", "----------------------------------------",
|
||||
"-----------");
|
||||
|
||||
foreach my $event_name (keys %unhandled) {
|
||||
printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
|
||||
}
|
||||
}
|
||||
|
||||
sub trace_unhandled
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain) = @_;
|
||||
|
||||
$unhandled{$event_name}++;
|
||||
}
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
#!/usr/bin/perl -w
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
|
||||
|
||||
# read/write top
|
||||
#
|
||||
# Periodically displays system-wide r/w call activity, broken down by
|
||||
# pid. If an [interval] arg is specified, the display will be
|
||||
# refreshed every [interval] seconds. The default interval is 3
|
||||
# seconds.
|
||||
|
||||
use 5.010000;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
||||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Util;
|
||||
use POSIX qw/SIGALRM SA_RESTART/;
|
||||
|
||||
my $default_interval = 3;
|
||||
my $nlines = 20;
|
||||
my $print_thread;
|
||||
my $print_pending = 0;
|
||||
|
||||
my %reads;
|
||||
my %writes;
|
||||
|
||||
my $interval = shift;
|
||||
if (!$interval) {
|
||||
$interval = $default_interval;
|
||||
}
|
||||
|
||||
sub syscalls::sys_exit_read
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$nr, $ret) = @_;
|
||||
|
||||
print_check();
|
||||
|
||||
if ($ret > 0) {
|
||||
$reads{$common_pid}{bytes_read} += $ret;
|
||||
} else {
|
||||
if (!defined ($reads{$common_pid}{bytes_read})) {
|
||||
$reads{$common_pid}{bytes_read} = 0;
|
||||
}
|
||||
$reads{$common_pid}{errors}{$ret}++;
|
||||
}
|
||||
}
|
||||
|
||||
sub syscalls::sys_enter_read
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$nr, $fd, $buf, $count) = @_;
|
||||
|
||||
print_check();
|
||||
|
||||
$reads{$common_pid}{bytes_requested} += $count;
|
||||
$reads{$common_pid}{total_reads}++;
|
||||
$reads{$common_pid}{comm} = $common_comm;
|
||||
}
|
||||
|
||||
sub syscalls::sys_exit_write
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$nr, $ret) = @_;
|
||||
|
||||
print_check();
|
||||
|
||||
if ($ret <= 0) {
|
||||
$writes{$common_pid}{errors}{$ret}++;
|
||||
}
|
||||
}
|
||||
|
||||
sub syscalls::sys_enter_write
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$nr, $fd, $buf, $count) = @_;
|
||||
|
||||
print_check();
|
||||
|
||||
$writes{$common_pid}{bytes_written} += $count;
|
||||
$writes{$common_pid}{total_writes}++;
|
||||
$writes{$common_pid}{comm} = $common_comm;
|
||||
}
|
||||
|
||||
sub trace_begin
|
||||
{
|
||||
my $sa = POSIX::SigAction->new(\&set_print_pending);
|
||||
$sa->flags(SA_RESTART);
|
||||
$sa->safe(1);
|
||||
POSIX::sigaction(SIGALRM, $sa) or die "Can't set SIGALRM handler: $!\n";
|
||||
alarm 1;
|
||||
}
|
||||
|
||||
sub trace_end
|
||||
{
|
||||
print_unhandled();
|
||||
print_totals();
|
||||
}
|
||||
|
||||
sub print_check()
|
||||
{
|
||||
if ($print_pending == 1) {
|
||||
$print_pending = 0;
|
||||
print_totals();
|
||||
}
|
||||
}
|
||||
|
||||
sub set_print_pending()
|
||||
{
|
||||
$print_pending = 1;
|
||||
alarm $interval;
|
||||
}
|
||||
|
||||
sub print_totals
|
||||
{
|
||||
my $count;
|
||||
|
||||
$count = 0;
|
||||
|
||||
clear_term();
|
||||
|
||||
printf("\nread counts by pid:\n\n");
|
||||
|
||||
printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
|
||||
"# reads", "bytes_req", "bytes_read");
|
||||
printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------",
|
||||
"----------", "----------", "----------");
|
||||
|
||||
foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=>
|
||||
($reads{$a}{bytes_read} || 0) } keys %reads) {
|
||||
my $comm = $reads{$pid}{comm} || "";
|
||||
my $total_reads = $reads{$pid}{total_reads} || 0;
|
||||
my $bytes_requested = $reads{$pid}{bytes_requested} || 0;
|
||||
my $bytes_read = $reads{$pid}{bytes_read} || 0;
|
||||
|
||||
printf("%6s %-20s %10s %10s %10s\n", $pid, $comm,
|
||||
$total_reads, $bytes_requested, $bytes_read);
|
||||
|
||||
if (++$count == $nlines) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
|
||||
printf("\nwrite counts by pid:\n\n");
|
||||
|
||||
printf("%6s %20s %10s %13s\n", "pid", "comm",
|
||||
"# writes", "bytes_written");
|
||||
printf("%6s %-20s %10s %13s\n", "------", "--------------------",
|
||||
"----------", "-------------");
|
||||
|
||||
foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=>
|
||||
($writes{$a}{bytes_written} || 0)} keys %writes) {
|
||||
my $comm = $writes{$pid}{comm} || "";
|
||||
my $total_writes = $writes{$pid}{total_writes} || 0;
|
||||
my $bytes_written = $writes{$pid}{bytes_written} || 0;
|
||||
|
||||
printf("%6s %-20s %10s %13s\n", $pid, $comm,
|
||||
$total_writes, $bytes_written);
|
||||
|
||||
if (++$count == $nlines) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
%reads = ();
|
||||
%writes = ();
|
||||
}
|
||||
|
||||
my %unhandled;
|
||||
|
||||
sub print_unhandled
|
||||
{
|
||||
if ((scalar keys %unhandled) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
print "\nunhandled events:\n\n";
|
||||
|
||||
printf("%-40s %10s\n", "event", "count");
|
||||
printf("%-40s %10s\n", "----------------------------------------",
|
||||
"-----------");
|
||||
|
||||
foreach my $event_name (keys %unhandled) {
|
||||
printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
|
||||
}
|
||||
}
|
||||
|
||||
sub trace_unhandled
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain) = @_;
|
||||
|
||||
$unhandled{$event_name}++;
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
#!/usr/bin/perl -w
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
|
||||
|
||||
# Display avg/min/max wakeup latency
|
||||
|
||||
# The common_* event handler fields are the most useful fields common to
|
||||
# all events. They don't necessarily correspond to the 'common_*' fields
|
||||
# in the status files. Those fields not available as handler params can
|
||||
# be retrieved via script functions of the form get_common_*().
|
||||
|
||||
use 5.010000;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
|
||||
use lib "./Perf-Trace-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Util;
|
||||
|
||||
my %last_wakeup;
|
||||
|
||||
my $max_wakeup_latency;
|
||||
my $min_wakeup_latency;
|
||||
my $total_wakeup_latency = 0;
|
||||
my $total_wakeups = 0;
|
||||
|
||||
sub sched::sched_switch
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$prev_comm, $prev_pid, $prev_prio, $prev_state, $next_comm, $next_pid,
|
||||
$next_prio) = @_;
|
||||
|
||||
my $wakeup_ts = $last_wakeup{$common_cpu}{ts};
|
||||
if ($wakeup_ts) {
|
||||
my $switch_ts = nsecs($common_secs, $common_nsecs);
|
||||
my $wakeup_latency = $switch_ts - $wakeup_ts;
|
||||
if ($wakeup_latency > $max_wakeup_latency) {
|
||||
$max_wakeup_latency = $wakeup_latency;
|
||||
}
|
||||
if ($wakeup_latency < $min_wakeup_latency) {
|
||||
$min_wakeup_latency = $wakeup_latency;
|
||||
}
|
||||
$total_wakeup_latency += $wakeup_latency;
|
||||
$total_wakeups++;
|
||||
}
|
||||
$last_wakeup{$common_cpu}{ts} = 0;
|
||||
}
|
||||
|
||||
sub sched::sched_wakeup
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain,
|
||||
$comm, $pid, $prio, $success, $target_cpu) = @_;
|
||||
|
||||
$last_wakeup{$target_cpu}{ts} = nsecs($common_secs, $common_nsecs);
|
||||
}
|
||||
|
||||
sub trace_begin
|
||||
{
|
||||
$min_wakeup_latency = 1000000000;
|
||||
$max_wakeup_latency = 0;
|
||||
}
|
||||
|
||||
sub trace_end
|
||||
{
|
||||
printf("wakeup_latency stats:\n\n");
|
||||
print "total_wakeups: $total_wakeups\n";
|
||||
if ($total_wakeups) {
|
||||
printf("avg_wakeup_latency (ns): %u\n",
|
||||
avg($total_wakeup_latency, $total_wakeups));
|
||||
} else {
|
||||
printf("avg_wakeup_latency (ns): N/A\n");
|
||||
}
|
||||
printf("min_wakeup_latency (ns): %u\n", $min_wakeup_latency);
|
||||
printf("max_wakeup_latency (ns): %u\n", $max_wakeup_latency);
|
||||
|
||||
print_unhandled();
|
||||
}
|
||||
|
||||
my %unhandled;
|
||||
|
||||
sub print_unhandled
|
||||
{
|
||||
if ((scalar keys %unhandled) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
print "\nunhandled events:\n\n";
|
||||
|
||||
printf("%-40s %10s\n", "event", "count");
|
||||
printf("%-40s %10s\n", "----------------------------------------",
|
||||
"-----------");
|
||||
|
||||
foreach my $event_name (keys %unhandled) {
|
||||
printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
|
||||
}
|
||||
}
|
||||
|
||||
sub trace_unhandled
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
|
||||
$common_pid, $common_comm, $common_callchain) = @_;
|
||||
|
||||
$unhandled{$event_name}++;
|
||||
}
|
||||
|
|
@ -79,7 +79,6 @@ make_no_jevents := NO_JEVENTS=1
|
|||
make_jevents_all := JEVENTS_ARCH=all
|
||||
make_no_bpf_skel := BUILD_BPF_SKEL=0
|
||||
make_gen_vmlinux_h := GEN_VMLINUX_H=1
|
||||
make_libperl := LIBPERL=1
|
||||
make_no_python_module := NO_PYTHON_MODULE=1
|
||||
make_no_scripts := NO_PYTHON_MODULE=1
|
||||
make_no_slang := NO_SLANG=1
|
||||
|
|
@ -175,7 +174,6 @@ run += make_no_jevents
|
|||
run += make_jevents_all
|
||||
run += make_no_bpf_skel
|
||||
run += make_gen_vmlinux_h
|
||||
run += make_libperl
|
||||
run += make_no_python_module
|
||||
run += make_no_scripts
|
||||
run += make_no_slang
|
||||
|
|
|
|||
|
|
@ -20,13 +20,6 @@ skip_if_no_cs_etm_event || exit 2
|
|||
set -e
|
||||
glb_err=1
|
||||
|
||||
# Relative path works whether it's installed or running from repo
|
||||
if [ -n "$PERF_EXEC_PATH" ] && [ -e "$PERF_EXEC_PATH/python/arm-cs-trace-disasm.py" ]; then
|
||||
script_path="$PERF_EXEC_PATH/python/arm-cs-trace-disasm.py"
|
||||
else
|
||||
script_path=$(dirname "$0")/../../../python/arm-cs-trace-disasm.py
|
||||
fi
|
||||
|
||||
# shellcheck source=lib/setup_python.sh
|
||||
. "$(dirname "$0")"/../lib/setup_python.sh
|
||||
$PYTHON -c "import perf" 2>/dev/null || {
|
||||
|
|
@ -56,8 +49,8 @@ branch_search='[[:space:]](bl|b(\.(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al)
|
|||
if [ "$(id -u)" == 0 ] && [ -e /proc/kcore ]; then
|
||||
echo "Testing kernel disassembly"
|
||||
perf record -o ${perfdata} -e cs_etm//k --kcore -Se -m,64K -- touch $file > /dev/null 2>&1
|
||||
$PYTHON ${script_path} -i ${perfdata} -d --stop-sample=2 \
|
||||
-k ${perfdata}/kcore_dir/kcore 2> /dev/null > ${file}
|
||||
perf script -i ${perfdata} arm-cs-trace-disasm -- \
|
||||
-d --stop-sample=2 -k ${perfdata}/kcore_dir/kcore 2> /dev/null > ${file}
|
||||
grep -q -E ${branch_search} ${file}
|
||||
echo "Found kernel branches"
|
||||
else
|
||||
|
|
@ -68,7 +61,8 @@ fi
|
|||
## Test user ##
|
||||
echo "Testing userspace disassembly"
|
||||
perf record -o ${perfdata} -e cs_etm//u -Se -m,64K -- touch $file > /dev/null 2>&1
|
||||
$PYTHON ${script_path} -i ${perfdata} -d --stop-sample=2 2> /dev/null > ${file}
|
||||
perf script -i ${perfdata} arm-cs-trace-disasm -- \
|
||||
-d --stop-sample=2 2> /dev/null > ${file}
|
||||
grep -q -E ${branch_search} ${file}
|
||||
echo "Found userspace branches"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
#!/bin/bash
|
||||
# perf script perl tests
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
set -e
|
||||
|
||||
# set PERF_EXEC_PATH to find scripts in the source directory
|
||||
perfdir=$(dirname "$0")/../..
|
||||
if [ -e "$perfdir/scripts/perl/Perf-Trace-Util" ]; then
|
||||
export PERF_EXEC_PATH=$perfdir
|
||||
fi
|
||||
|
||||
|
||||
perfdata=$(mktemp /tmp/__perf_test_script_perl.perf.data.XXXXX)
|
||||
generated_script=$(mktemp /tmp/__perf_test_script.XXXXX.pl)
|
||||
|
||||
cleanup() {
|
||||
rm -f "${perfdata}"
|
||||
rm -f "${generated_script}"
|
||||
trap - EXIT TERM INT
|
||||
}
|
||||
|
||||
trap_cleanup() {
|
||||
echo "Unexpected signal in ${FUNCNAME[1]}"
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
trap trap_cleanup TERM INT
|
||||
trap cleanup EXIT
|
||||
|
||||
check_perl_support() {
|
||||
if perf check feature -q libperl; then
|
||||
return 0
|
||||
fi
|
||||
echo "perf script perl test [Skipped: no libperl support]"
|
||||
return 2
|
||||
}
|
||||
|
||||
test_script() {
|
||||
local event_name=$1
|
||||
local expected_output=$2
|
||||
local record_opts=$3
|
||||
|
||||
echo "Testing event: $event_name"
|
||||
|
||||
# Try to record. If this fails, it might be permissions or lack of support.
|
||||
# We return 2 to indicate "skip this event" rather than "fail test".
|
||||
if ! perf record -o "${perfdata}" -e "$event_name" $record_opts -- perf test -w thloop > /dev/null 2>&1; then
|
||||
echo "perf script perl test [Skipped: failed to record $event_name]"
|
||||
return 2
|
||||
fi
|
||||
|
||||
echo "Generating perl script..."
|
||||
if ! perf script -i "${perfdata}" -g "${generated_script}"; then
|
||||
echo "perf script perl test [Failed: script generation for $event_name]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${generated_script}" ]; then
|
||||
echo "perf script perl test [Failed: script not generated for $event_name]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Executing perl script..."
|
||||
output=$(perf script -i "${perfdata}" -s "${generated_script}" 2>&1)
|
||||
|
||||
if echo "$output" | grep -q "$expected_output"; then
|
||||
echo "perf script perl test [Success: $event_name triggered $expected_output]"
|
||||
return 0
|
||||
else
|
||||
echo "perf script perl test [Failed: $event_name did not trigger $expected_output]"
|
||||
echo "Output was:"
|
||||
echo "$output" | head -n 20
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_perl_support || exit 2
|
||||
|
||||
# Try tracepoint first
|
||||
test_script "sched:sched_switch" "sched::sched_switch" "-c 1" && res=0 || res=$?
|
||||
|
||||
if [ $res -eq 0 ]; then
|
||||
exit 0
|
||||
elif [ $res -eq 1 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If tracepoint skipped (res=2), try task-clock
|
||||
# For generic events like task-clock, the generated script uses process_event()
|
||||
# which dumps data using Data::Dumper. We check for "$VAR1" which is standard Dumper output.
|
||||
test_script "task-clock" "\$VAR1" "-c 100" && res=0 || res=$?
|
||||
|
||||
if [ $res -eq 0 ]; then
|
||||
exit 0
|
||||
elif [ $res -eq 1 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If both skipped
|
||||
echo "perf script perl test [Skipped: Could not record tracepoint or task-clock]"
|
||||
exit 2
|
||||
|
|
@ -68,7 +68,7 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Run the script
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script check-perf-trace -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "File mode test failed."
|
||||
err=1
|
||||
elif ! grep -q "in trace_begin" "${temp_out}" || \
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Run the script with some filters to validate filtering logic
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script compaction-times -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "File mode default test failed."
|
||||
err=1
|
||||
elif ! grep -q "total:" "${temp_out}"; then
|
||||
|
|
@ -66,7 +66,7 @@ test_file_mode() {
|
|||
err=1
|
||||
fi
|
||||
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" "0-0" > "${temp_out}"; then
|
||||
if ! perf script compaction-times -i "${temp_data}" "0-0" > "${temp_out}"; then
|
||||
echo "File mode strict PID filter test failed."
|
||||
err=1
|
||||
elif ! grep -q "total:" "${temp_out}"; then
|
||||
|
|
@ -74,7 +74,7 @@ test_file_mode() {
|
|||
err=1
|
||||
fi
|
||||
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" "sleep" > "${temp_out}"; then
|
||||
if ! perf script compaction-times -i "${temp_data}" "sleep" > "${temp_out}"; then
|
||||
echo "File mode comm filter test failed."
|
||||
err=1
|
||||
elif ! grep -q "total:" "${temp_out}"; then
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Run the script
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" -d "${temp_db}" > "${temp_dir}/perf.out" 2>&1; then
|
||||
if ! perf script event_analyzing_sample -i "${temp_data}" \
|
||||
-d "${temp_db}" > "${temp_dir}/perf.out" 2>&1; then
|
||||
echo "File mode test failed."
|
||||
err=1
|
||||
elif ! grep -q "Statistics about the general events" "${temp_dir}/perf.out" || \
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ test_file_mode() {
|
|||
echo "Testing export-to-postgresql.py..."
|
||||
|
||||
# Verify that invalid URI / key-value database names are rejected
|
||||
if "$PYTHON" "$script_path" -i /dev/null -o "dbname=foo host=evil" >/dev/null 2>&1; then
|
||||
if perf script export-to-postgresql -i /dev/null \
|
||||
-o "dbname=foo host=evil" >/dev/null 2>&1; then
|
||||
echo "Connection parameter injection check failed."
|
||||
err=1
|
||||
fi
|
||||
|
|
@ -71,7 +72,7 @@ test_file_mode() {
|
|||
psql -c "DROP DATABASE IF EXISTS ${temp_db}" postgres >/dev/null 2>&1 || true
|
||||
|
||||
# Run the script
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" -o "${temp_db}" >/dev/null; then
|
||||
if ! perf script export-to-postgresql -i "${temp_data}" -o "${temp_db}" >/dev/null; then
|
||||
echo "File mode test failed."
|
||||
err=1
|
||||
else
|
||||
|
|
@ -101,7 +102,8 @@ test_intel_pt() {
|
|||
fi
|
||||
|
||||
# Run the script with --itrace cr to synthesize call_returns
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" -o "${temp_db}" --itrace cr >/dev/null; then
|
||||
if ! perf script export-to-postgresql -i "${temp_data}" \
|
||||
-o "${temp_db}" --itrace cr >/dev/null; then
|
||||
echo "intel_pt file mode test failed."
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Run the script
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" -o "${temp_db}" >/dev/null; then
|
||||
if ! perf script export-to-sqlite -i "${temp_data}" -o "${temp_db}" >/dev/null; then
|
||||
echo "File mode test failed."
|
||||
err=1
|
||||
else
|
||||
|
|
@ -86,7 +86,7 @@ test_intel_pt() {
|
|||
fi
|
||||
|
||||
# Run the script with --itrace cr to synthesize call_returns
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" -o "${temp_db}" --itrace cr; then
|
||||
if ! perf script export-to-sqlite -i "${temp_data}" -o "${temp_db}" --itrace cr; then
|
||||
echo "intel_pt file mode test failed."
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -63,14 +63,14 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Run the script and check output
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script failed-syscalls-by-pid -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "failed-syscalls-by-pid test failed."
|
||||
err=1
|
||||
elif ! grep -n -q "err = ENOENT" "${temp_out}"; then
|
||||
echo "Failed to find expected failed syscalls"
|
||||
cat "${temp_out}"
|
||||
err=1
|
||||
elif ! "$PYTHON" "$script_path" -i "${temp_data}" "ls" > "${temp_out}.comm" || \
|
||||
elif ! perf script failed-syscalls-by-pid -i "${temp_data}" "ls" > "${temp_out}.comm" || \
|
||||
! grep -q "err = ENOENT" "${temp_out}.comm"; then
|
||||
echo "failed-syscalls-by-pid comm filter test failed."
|
||||
cat "${temp_out}.comm"
|
||||
|
|
@ -78,7 +78,8 @@ test_file_mode() {
|
|||
else
|
||||
ls_pid=$(sed -n 's/^ls \[\([0-9][0-9]*\)\].*/\1/p' "${temp_out}.comm" | head -n 1)
|
||||
if [ -z "${ls_pid}" ] || \
|
||||
! "$PYTHON" "$script_path" -i "${temp_data}" "${ls_pid}" > "${temp_out}.pid" || \
|
||||
! perf script failed-syscalls-by-pid -i "${temp_data}" \
|
||||
"${ls_pid}" > "${temp_out}.pid" || \
|
||||
! grep -q "err = ENOENT" "${temp_out}.pid"; then
|
||||
echo "failed-syscalls-by-pid PID filter test failed."
|
||||
cat "${temp_out}.pid" 2>/dev/null || true
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script failed-syscalls -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "failed-syscalls.py test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Run the script in file mode and validate JSON output
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" -f json -o "${temp_json}" >/dev/null; then
|
||||
if ! perf script flamegraph -i "${temp_data}" -f json -o "${temp_json}" >/dev/null; then
|
||||
echo "File mode JSON test failed."
|
||||
err=1
|
||||
else
|
||||
|
|
@ -76,7 +76,7 @@ test_file_mode() {
|
|||
# Run the script in pipe ('-') mode and validate JSON output
|
||||
rm -f "${temp_json}"
|
||||
if ! perf record -g -o - -- perf test -w noploop 2>/dev/null | \
|
||||
"$PYTHON" "$script_path" -i - -f json -o "${temp_json}" >/dev/null; then
|
||||
perf script flamegraph -i - -f json -o "${temp_json}" >/dev/null; then
|
||||
echo "Pipe stdin JSON mode test failed."
|
||||
err=1
|
||||
else
|
||||
|
|
@ -84,7 +84,7 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Run the script, dump as html to temp_html using MINIMAL_HTML fallback
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" -f html \
|
||||
if ! perf script flamegraph -i "${temp_data}" -f html \
|
||||
--template /nonexistent/template.html -o "${temp_html}" >/dev/null 2>&1; then
|
||||
echo "File mode HTML test failed."
|
||||
err=1
|
||||
|
|
@ -105,7 +105,7 @@ const opts = /** @options_json **/;
|
|||
const data = /** @flamegraph_json **/;
|
||||
</script></body></html>
|
||||
EOF
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" -f html --template "${temp_tpl}" \
|
||||
if ! perf script flamegraph -i "${temp_data}" -f html --template "${temp_tpl}" \
|
||||
--colorscheme blue-green -o "${temp_html}" >/dev/null 2>&1; then
|
||||
echo "Custom template HTML test failed."
|
||||
err=1
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ test_file_mode() {
|
|||
return
|
||||
fi
|
||||
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script futex-contention -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "File mode test failed."
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Run the script in save-only mode with custom product and category colors
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" \
|
||||
if ! perf script gecko -i "${temp_data}" \
|
||||
--product "perf-test-product" --user-color blue --kernel-color red \
|
||||
--save-only "${temp_json}" >/dev/null; then
|
||||
echo "File mode test failed."
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ test_intel_pt() {
|
|||
fi
|
||||
|
||||
# Run the script and check output
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script intel-pt-events -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "intel-pt-events.py test failed."
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -87,7 +87,8 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Run the script with custom --iomem
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" --iomem "${temp_iomem}" > "${temp_out}" || \
|
||||
if ! perf script mem-phys-addr -i "${temp_data}" \
|
||||
--iomem "${temp_iomem}" > "${temp_out}" || \
|
||||
[ ! -s "${temp_out}" ]; then
|
||||
echo "File mode test failed."
|
||||
err=1
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes and outputs table header
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script net_dropmonitor -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "net_dropmonitor.py test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ if ! perf record -e skb:kfree_skb -a -o "${temp_data}" \
|
|||
fi
|
||||
|
||||
# Check that the script executes
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script netdev-times -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "netdev-times.py test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes on recorded perf.data
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script powerpc-hcalls -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "powerpc-hcalls.py test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes - filtering for "dd" since that's what we ran
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" "dd" > "${temp_out}"; then
|
||||
if ! perf script rw-by-file -i "${temp_data}" "dd" > "${temp_out}"; then
|
||||
echo "rw-by-file.py test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script rw-by-pid -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "rw-by-pid.py test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script rwtop -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "rwtop.py test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes
|
||||
if ! "$PYTHON" "$script_path" -v --no-gui -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script sched-migration -v --no-gui -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "sched-migration.py test failed"
|
||||
err=1
|
||||
elif [ "$has_sched" -eq 1 ] && ! grep -q "Timeslices:" "${temp_out}"; then
|
||||
|
|
|
|||
|
|
@ -56,13 +56,13 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script sctop -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "sctop.py test failed"
|
||||
err=1
|
||||
elif ! grep -E -q "[0-9]+$" "${temp_out}"; then
|
||||
echo "Failed to find metric data rows in default run"
|
||||
err=1
|
||||
elif ! "$PYTHON" "$script_path" -i "${temp_data}" sleep 1 > "${temp_out}"; then
|
||||
elif ! perf script sctop -i "${temp_data}" sleep 1 > "${temp_out}"; then
|
||||
echo "sctop.py comm+interval test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes with default options
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script stackcollapse -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "stackcollapse.py test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
@ -61,12 +61,12 @@ else
|
|||
fi
|
||||
|
||||
# Test CLI flags (--include-pid, --include-tid, --tidy-java, --kernel) and BrokenPipeError
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" \
|
||||
if ! perf script stackcollapse -i "${temp_data}" \
|
||||
--include-pid --include-tid --tidy-java --kernel | head -n 1 > "${temp_out}" || \
|
||||
[ ! -s "${temp_out}" ]; then
|
||||
echo "stackcollapse.py options/pipe test failed"
|
||||
err=1
|
||||
elif ! "$PYTHON" "$script_path" -i "${temp_data}" --no-comm > "${temp_out}" || \
|
||||
elif ! perf script stackcollapse -i "${temp_data}" --no-comm > "${temp_out}" || \
|
||||
[ ! -s "${temp_out}" ]; then
|
||||
echo "stackcollapse.py --no-comm test failed"
|
||||
err=1
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ test_live_mode() {
|
|||
ran=1
|
||||
|
||||
# Run live mode for 1 interval in the background, give it a tiny sleep, then interrupt
|
||||
"$PYTHON" "$script_path" -I 0.1 -p "$workload_pid" > "${temp_out}" &
|
||||
perf script stat-cpi -I 0.1 -p "$workload_pid" > "${temp_out}" &
|
||||
pid=$!
|
||||
sleep 0.5
|
||||
kill -INT "$pid" 2>/dev/null || true
|
||||
|
|
@ -96,7 +96,7 @@ test_file_mode() {
|
|||
fi
|
||||
ran=1
|
||||
|
||||
out=$("$PYTHON" "$script_path" -i "${temp_data}")
|
||||
out=$(perf script stat-cpi -i "${temp_data}")
|
||||
if ! echo "$out" | grep -q "cpi"; then
|
||||
echo "File mode test failed."
|
||||
err=1
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ test_file_mode() {
|
|||
-- sleep 0.5 >/dev/null 2>&1 || \
|
||||
{ echo "Skipping test, perf record failed"; exit 2; }
|
||||
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script syscall-counts-by-pid -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "File mode test failed."
|
||||
err=1
|
||||
elif ! grep -E -q "^ [a-zA-Z0-9_]+ +[0-9]+$" "${temp_out}"; then
|
||||
|
|
@ -64,7 +64,7 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Test with a comm argument
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" "sleep" > "${temp_out}"; then
|
||||
if ! perf script syscall-counts-by-pid -i "${temp_data}" "sleep" > "${temp_out}"; then
|
||||
echo "Comm filter test failed."
|
||||
err=1
|
||||
elif ! grep -E -q "^ [a-zA-Z0-9_]+ +[0-9]+$" "${temp_out}"; then
|
||||
|
|
@ -76,7 +76,8 @@ test_file_mode() {
|
|||
|
||||
# Extract sleep PID from "sleep [<pid>]" header and test with a numeric PID filter argument
|
||||
sleep_pid=$(sed -n 's/^sleep \[\([0-9]\+\)\]$/\1/p' "${temp_out}" | head -n 1)
|
||||
if [ -z "${sleep_pid}" ] || ! "$PYTHON" "$script_path" -i "${temp_data}" "${sleep_pid}" > "${temp_out}"; then
|
||||
if [ -z "${sleep_pid}" ] || \
|
||||
! perf script syscall-counts-by-pid -i "${temp_data}" "${sleep_pid}" > "${temp_out}"; then
|
||||
echo "PID filter test failed."
|
||||
err=1
|
||||
elif ! grep -E -q "^ [a-zA-Z0-9_]+ +[0-9]+$" "${temp_out}"; then
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ test_file_mode() {
|
|||
exit 2
|
||||
fi
|
||||
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script syscall-counts -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "File mode test failed."
|
||||
err=1
|
||||
elif ! grep -E -q "^[a-zA-Z0-9_]+ +[0-9]+$" "${temp_out}"; then
|
||||
|
|
@ -65,7 +65,7 @@ test_file_mode() {
|
|||
fi
|
||||
|
||||
# Test with a comm argument
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" "sleep" > "${temp_out}"; then
|
||||
if ! perf script syscall-counts -i "${temp_data}" "sleep" > "${temp_out}"; then
|
||||
echo "Comm filter test failed."
|
||||
err=1
|
||||
elif ! grep -E -q "^[a-zA-Z0-9_]+ +[0-9]+$" "${temp_out}"; then
|
||||
|
|
|
|||
|
|
@ -11,16 +11,6 @@ csv="$tmpdir/csv"
|
|||
csvsummary="$tmpdir/csvsummary"
|
||||
err=0
|
||||
|
||||
# Set up perfdir and PERF_EXEC_PATH
|
||||
if [ "x$PERF_EXEC_PATH" = "x" ]; then
|
||||
perfdir="$(dirname "$0")/../.."
|
||||
if [ -f "$perfdir/python/task-analyzer.py" ]; then
|
||||
export PERF_EXEC_PATH="$perfdir"
|
||||
fi
|
||||
else
|
||||
perfdir="$PERF_EXEC_PATH"
|
||||
fi
|
||||
|
||||
# Disable lsan to avoid warnings about python memory leaks.
|
||||
export ASAN_OPTIONS=detect_leaks=0
|
||||
|
||||
|
|
@ -81,17 +71,17 @@ prepare_perf_data() {
|
|||
# check standard inkvokation with no arguments
|
||||
test_basic() {
|
||||
out="$tmpdir/perf.out"
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" > "$out"
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata}"
|
||||
perf script -i "${perfdata}" task-analyzer > "$out"
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer"
|
||||
find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}"
|
||||
}
|
||||
|
||||
test_ns_rename(){
|
||||
out="$tmpdir/perf.out"
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" \
|
||||
perf script -i "${perfdata}" task-analyzer \
|
||||
--ns \
|
||||
--rename-comms-by-tids 0:random > "$out"
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} \
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer \
|
||||
--ns \
|
||||
--rename-comms-by-tids 0:random"
|
||||
find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}"
|
||||
|
|
@ -99,20 +89,20 @@ test_ns_rename(){
|
|||
|
||||
test_ms_filtertasks_highlight(){
|
||||
out="$tmpdir/perf.out"
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" \
|
||||
perf script -i "${perfdata}" task-analyzer \
|
||||
--ms --filter-tasks perf --highlight-tasks perf \
|
||||
> "$out"
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} \
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer \
|
||||
--ms --filter-tasks perf --highlight-tasks perf"
|
||||
find_str_or_fail "Comm" "$out" "${FUNCNAME[0]}"
|
||||
}
|
||||
|
||||
test_extended_times_timelimit_limittasks() {
|
||||
out="$tmpdir/perf.out"
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --extended-times \
|
||||
perf script -i "${perfdata}" task-analyzer --extended-times \
|
||||
--time-limit :99999 \
|
||||
--limit-to-tasks perf > "$out"
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} \
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer \
|
||||
--extended-times \
|
||||
--time-limit :99999 --limit-to-tasks perf"
|
||||
find_str_or_fail "Out-Out" "$out" "${FUNCNAME[0]}"
|
||||
|
|
@ -120,30 +110,30 @@ test_extended_times_timelimit_limittasks() {
|
|||
|
||||
test_summary() {
|
||||
out="$tmpdir/perf.out"
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --summary > "$out"
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --summary"
|
||||
perf script -i "${perfdata}" task-analyzer --summary > "$out"
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer --summary"
|
||||
find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}"
|
||||
}
|
||||
|
||||
test_summaryextended() {
|
||||
out="$tmpdir/perf.out"
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --summary-extended > "$out"
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --summary-extended"
|
||||
perf script -i "${perfdata}" task-analyzer --summary-extended > "$out"
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer --summary-extended"
|
||||
find_str_or_fail "Inter Task Times" "$out" "${FUNCNAME[0]}"
|
||||
}
|
||||
|
||||
test_summaryonly() {
|
||||
out="$tmpdir/perf.out"
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --summary-only > "$out"
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --summary-only"
|
||||
perf script -i "${perfdata}" task-analyzer --summary-only > "$out"
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer --summary-only"
|
||||
find_str_or_fail "Summary" "$out" "${FUNCNAME[0]}"
|
||||
}
|
||||
|
||||
test_extended_times_summary_ns() {
|
||||
out="$tmpdir/perf.out"
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --extended-times \
|
||||
perf script -i "${perfdata}" task-analyzer --extended-times \
|
||||
--summary --ns > "$out"
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} \
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer \
|
||||
--extended-times \
|
||||
--summary \
|
||||
--ns"
|
||||
|
|
@ -152,34 +142,34 @@ test_extended_times_summary_ns() {
|
|||
}
|
||||
|
||||
test_csv() {
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" --csv "${csv}" > /dev/null
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} --csv ${csv}"
|
||||
perf script -i "${perfdata}" task-analyzer --csv "${csv}" > /dev/null
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer --csv ${csv}"
|
||||
find_str_or_fail "Comm;" "${csv}" "${FUNCNAME[0]}"
|
||||
}
|
||||
|
||||
test_csv_extended_times() {
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" \
|
||||
perf script -i "${perfdata}" task-analyzer \
|
||||
--csv "${csv}" \
|
||||
--extended-times > /dev/null
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} \
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer \
|
||||
--csv ${csv} \
|
||||
--extended-times"
|
||||
find_str_or_fail "Out-Out;" "${csv}" "${FUNCNAME[0]}"
|
||||
}
|
||||
|
||||
test_csvsummary() {
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" \
|
||||
perf script -i "${perfdata}" task-analyzer \
|
||||
--csv-summary "${csvsummary}" > /dev/null
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} \
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer \
|
||||
--csv-summary ${csvsummary}"
|
||||
find_str_or_fail "Comm;" "${csvsummary}" "${FUNCNAME[0]}"
|
||||
}
|
||||
|
||||
test_csvsummary_extended() {
|
||||
$PYTHON $perfdir/python/task-analyzer.py -i "${perfdata}" \
|
||||
perf script -i "${perfdata}" task-analyzer \
|
||||
--csv-summary "${csvsummary}" --summary-extended \
|
||||
>/dev/null
|
||||
check_exec_0 "$PYTHON $perfdir/python/task-analyzer.py -i ${perfdata} \
|
||||
check_exec_0 "perf script -i ${perfdata} task-analyzer \
|
||||
--csv-summary ${csvsummary} --summary-extended"
|
||||
find_str_or_fail "Out-Out;" "${csvsummary}" "${FUNCNAME[0]}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ if [ ! -s "${temp_data}" ]; then
|
|||
fi
|
||||
|
||||
# Check that the script executes
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}"; then
|
||||
if ! perf script wakeup-latency -i "${temp_data}" > "${temp_out}"; then
|
||||
echo "wakeup-latency.py test failed"
|
||||
err=1
|
||||
else
|
||||
|
|
@ -71,7 +71,7 @@ fi
|
|||
|
||||
# Also test zero-wakeups / unhandled events path to verify division-by-zero protection
|
||||
if perf record -e cycles -o "${temp_data}" -- perf test -w noploop >/dev/null 2>&1; then
|
||||
if ! "$PYTHON" "$script_path" -i "${temp_data}" > "${temp_out}" || \
|
||||
if ! perf script wakeup-latency -i "${temp_data}" > "${temp_out}" || \
|
||||
! grep -q "avg_wakeup_latency (ns): N/A" "${temp_out}"; then
|
||||
echo "wakeup-latency zero-wakeups guard test failed"
|
||||
err=1
|
||||
|
|
|
|||
|
|
@ -86,75 +86,6 @@ static int scripts_config(const char *var, const char *value, void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some scripts specify the required events in their "xxx-record" file,
|
||||
* this function will check if the events in perf.data match those
|
||||
* mentioned in the "xxx-record".
|
||||
*
|
||||
* Fixme: All existing "xxx-record" are all in good formats "-e event ",
|
||||
* which is covered well now. And new parsing code should be added to
|
||||
* cover the future complex formats like event groups etc.
|
||||
*/
|
||||
static int check_ev_match(int dir_fd, const char *scriptname, struct perf_session *session)
|
||||
{
|
||||
char line[BUFSIZ];
|
||||
FILE *fp;
|
||||
|
||||
{
|
||||
char filename[NAME_MAX + 5];
|
||||
int fd;
|
||||
|
||||
scnprintf(filename, sizeof(filename), "bin/%s-record", scriptname);
|
||||
fd = openat(dir_fd, filename, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
fp = fdopen(fd, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
char *p = skip_spaces(line);
|
||||
|
||||
if (*p == '#')
|
||||
continue;
|
||||
|
||||
while (strlen(p)) {
|
||||
int match, len;
|
||||
struct evsel *pos;
|
||||
char evname[128];
|
||||
|
||||
p = strstr(p, "-e");
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
p += 2;
|
||||
p = skip_spaces(p);
|
||||
len = strcspn(p, " \t");
|
||||
if (!len)
|
||||
break;
|
||||
|
||||
snprintf(evname, sizeof(evname), "%.*s", (int)len, p);
|
||||
|
||||
match = 0;
|
||||
evlist__for_each_entry(session->evlist, pos) {
|
||||
if (evsel__name_is(pos, evname)) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return -1 if none is found, otherwise the actual scripts number.
|
||||
*
|
||||
|
|
@ -162,95 +93,18 @@ static int check_ev_match(int dir_fd, const char *scriptname, struct perf_sessio
|
|||
* will list all statically runnable scripts, select one, execute it and
|
||||
* show the output in a perf browser.
|
||||
*/
|
||||
static int find_scripts(char **scripts_array, char **scripts_path_array, int num,
|
||||
int pathlen)
|
||||
static int find_scripts(char **scripts_array __maybe_unused,
|
||||
char **scripts_path_array __maybe_unused, int num __maybe_unused,
|
||||
int pathlen __maybe_unused)
|
||||
{
|
||||
int namelen;
|
||||
struct dirent *script_dirent, *lang_dirent;
|
||||
int scripts_dir_fd, lang_dir_fd;
|
||||
DIR *scripts_dir, *lang_dir;
|
||||
struct perf_session *session;
|
||||
struct perf_data data = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
char *temp;
|
||||
int i = 0;
|
||||
const char *exec_path = get_argv_exec_path();
|
||||
|
||||
session = perf_session__new(&data, NULL);
|
||||
if (IS_ERR(session))
|
||||
return PTR_ERR(session);
|
||||
|
||||
{
|
||||
char scripts_path[PATH_MAX];
|
||||
|
||||
snprintf(scripts_path, sizeof(scripts_path), "%s/scripts", exec_path);
|
||||
scripts_dir_fd = open(scripts_path, O_DIRECTORY);
|
||||
}
|
||||
if (scripts_dir_fd != -1) {
|
||||
scripts_dir = fdopendir(scripts_dir_fd);
|
||||
if (scripts_dir) {
|
||||
while ((lang_dirent = readdir(scripts_dir)) != NULL) {
|
||||
if (lang_dirent->d_type != DT_DIR &&
|
||||
(lang_dirent->d_type == DT_UNKNOWN &&
|
||||
!is_directory_at(scripts_dir_fd, lang_dirent->d_name)))
|
||||
continue;
|
||||
if (!strcmp(lang_dirent->d_name, ".") ||
|
||||
!strcmp(lang_dirent->d_name, ".."))
|
||||
continue;
|
||||
|
||||
if (strstr(lang_dirent->d_name, "python"))
|
||||
continue;
|
||||
|
||||
lang_dir_fd = openat(scripts_dir_fd, lang_dirent->d_name,
|
||||
O_DIRECTORY);
|
||||
if (lang_dir_fd == -1)
|
||||
continue;
|
||||
lang_dir = fdopendir(lang_dir_fd);
|
||||
if (!lang_dir) {
|
||||
close(lang_dir_fd);
|
||||
continue;
|
||||
}
|
||||
while ((script_dirent = readdir(lang_dir)) != NULL) {
|
||||
if (script_dirent->d_type == DT_DIR)
|
||||
continue;
|
||||
if (script_dirent->d_type == DT_UNKNOWN &&
|
||||
is_directory_at(lang_dir_fd, script_dirent->d_name))
|
||||
continue;
|
||||
/* Skip those real time scripts: xxxtop.p[yl] */
|
||||
if (strstr(script_dirent->d_name, "top."))
|
||||
continue;
|
||||
if (i >= num)
|
||||
break;
|
||||
scnprintf(scripts_path_array[i], pathlen,
|
||||
"%s/scripts/%s/%s", exec_path,
|
||||
lang_dirent->d_name,
|
||||
script_dirent->d_name);
|
||||
temp = strrchr(script_dirent->d_name, '.');
|
||||
namelen = temp ? (int)(temp - script_dirent->d_name)
|
||||
: (int)strlen(script_dirent->d_name);
|
||||
|
||||
if (namelen >= SCRIPT_NAMELEN)
|
||||
namelen = SCRIPT_NAMELEN - 1;
|
||||
snprintf(scripts_array[i], namelen + 1, "%s",
|
||||
script_dirent->d_name);
|
||||
|
||||
if (check_ev_match(lang_dir_fd, scripts_array[i], session))
|
||||
continue;
|
||||
|
||||
i++;
|
||||
}
|
||||
closedir(lang_dir);
|
||||
}
|
||||
closedir(scripts_dir);
|
||||
} else {
|
||||
close(scripts_dir_fd);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_PYTHON_MODULE_SUPPORT
|
||||
{
|
||||
int namelen;
|
||||
struct dirent *script_dirent;
|
||||
char *temp;
|
||||
const char *exec_path = get_argv_exec_path();
|
||||
char py_scripts_path[PATH_MAX];
|
||||
int py_scripts_dir_fd;
|
||||
DIR *py_scripts_dir;
|
||||
|
|
@ -299,7 +153,6 @@ static int find_scripts(char **scripts_array, char **scripts_path_array, int num
|
|||
}
|
||||
}
|
||||
#endif
|
||||
perf_session__delete(session);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ perf-util-y += tool_pmu.o
|
|||
perf-util-y += tp_pmu.o
|
||||
perf-util-y += svghelper.o
|
||||
perf-util-y += trace-event-info.o
|
||||
perf-util-y += trace-event-scripting.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-parse.o
|
||||
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-read.o
|
||||
|
|
@ -236,8 +235,6 @@ endif
|
|||
|
||||
perf-util-y += data-convert-json.o
|
||||
|
||||
perf-util-y += scripting-engines/
|
||||
|
||||
perf-util-$(CONFIG_ZLIB) += zlib.o
|
||||
perf-util-$(CONFIG_LZMA) += lzma.o
|
||||
perf-util-$(CONFIG_ZSTD) += zstd.o
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
ifeq ($(CONFIG_LIBTRACEEVENT),y)
|
||||
perf-util-$(CONFIG_LIBPERL) += trace-event-perl.o
|
||||
endif
|
||||
|
||||
CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-nested-externs -Wno-undef -Wno-switch-default -Wno-bad-function-cast -Wno-declaration-after-statement -Wno-switch-enum -Wno-thread-safety-analysis
|
||||
|
|
@ -1,770 +0,0 @@
|
|||
/*
|
||||
* trace-event-perl. Feed perf script events to an embedded Perl interpreter.
|
||||
*
|
||||
* Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.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 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 <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/time64.h>
|
||||
#include <event-parse.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
/* perl needs the following define, right after including stdbool.h */
|
||||
#define HAS_BOOL
|
||||
#include <EXTERN.h>
|
||||
#include <perl.h>
|
||||
|
||||
#include "../callchain.h"
|
||||
#include "../dso.h"
|
||||
#include "../machine.h"
|
||||
#include "../map.h"
|
||||
#include "../symbol.h"
|
||||
#include "../thread.h"
|
||||
#include "../event.h"
|
||||
#include "../trace-event.h"
|
||||
#include "../evsel.h"
|
||||
#include "../debug.h"
|
||||
|
||||
void boot_Perf__Trace__Context(pTHX_ CV *cv);
|
||||
void boot_DynaLoader(pTHX_ CV *cv);
|
||||
typedef PerlInterpreter * INTERP;
|
||||
|
||||
void xs_init(pTHX);
|
||||
|
||||
void xs_init(pTHX)
|
||||
{
|
||||
const char *file = __FILE__;
|
||||
dXSUB_SYS;
|
||||
|
||||
newXS("Perf::Trace::Context::bootstrap", boot_Perf__Trace__Context,
|
||||
file);
|
||||
newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
|
||||
}
|
||||
|
||||
INTERP my_perl;
|
||||
|
||||
#define TRACE_EVENT_TYPE_MAX \
|
||||
((1 << (sizeof(unsigned short) * 8)) - 1)
|
||||
|
||||
extern struct scripting_context *scripting_context;
|
||||
|
||||
static char *cur_field_name;
|
||||
static int zero_flag_atom;
|
||||
|
||||
static void define_symbolic_value(const char *ev_name,
|
||||
const char *field_name,
|
||||
const char *field_value,
|
||||
const char *field_str)
|
||||
{
|
||||
unsigned long long value;
|
||||
dSP;
|
||||
|
||||
value = eval_flag(field_value);
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
PUSHMARK(SP);
|
||||
|
||||
XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
|
||||
XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
|
||||
XPUSHs(sv_2mortal(newSVuv(value)));
|
||||
XPUSHs(sv_2mortal(newSVpv(field_str, 0)));
|
||||
|
||||
PUTBACK;
|
||||
if (get_cv("main::define_symbolic_value", 0))
|
||||
call_pv("main::define_symbolic_value", G_SCALAR);
|
||||
SPAGAIN;
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
static void define_symbolic_values(struct tep_print_flag_sym *field,
|
||||
const char *ev_name,
|
||||
const char *field_name)
|
||||
{
|
||||
define_symbolic_value(ev_name, field_name, field->value, field->str);
|
||||
if (field->next)
|
||||
define_symbolic_values(field->next, ev_name, field_name);
|
||||
}
|
||||
|
||||
static void define_symbolic_field(const char *ev_name,
|
||||
const char *field_name)
|
||||
{
|
||||
dSP;
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
PUSHMARK(SP);
|
||||
|
||||
XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
|
||||
XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
|
||||
|
||||
PUTBACK;
|
||||
if (get_cv("main::define_symbolic_field", 0))
|
||||
call_pv("main::define_symbolic_field", G_SCALAR);
|
||||
SPAGAIN;
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
static void define_flag_value(const char *ev_name,
|
||||
const char *field_name,
|
||||
const char *field_value,
|
||||
const char *field_str)
|
||||
{
|
||||
unsigned long long value;
|
||||
dSP;
|
||||
|
||||
value = eval_flag(field_value);
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
PUSHMARK(SP);
|
||||
|
||||
XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
|
||||
XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
|
||||
XPUSHs(sv_2mortal(newSVuv(value)));
|
||||
XPUSHs(sv_2mortal(newSVpv(field_str, 0)));
|
||||
|
||||
PUTBACK;
|
||||
if (get_cv("main::define_flag_value", 0))
|
||||
call_pv("main::define_flag_value", G_SCALAR);
|
||||
SPAGAIN;
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
static void define_flag_values(struct tep_print_flag_sym *field,
|
||||
const char *ev_name,
|
||||
const char *field_name)
|
||||
{
|
||||
define_flag_value(ev_name, field_name, field->value, field->str);
|
||||
if (field->next)
|
||||
define_flag_values(field->next, ev_name, field_name);
|
||||
}
|
||||
|
||||
static void define_flag_field(const char *ev_name,
|
||||
const char *field_name,
|
||||
const char *delim)
|
||||
{
|
||||
dSP;
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
PUSHMARK(SP);
|
||||
|
||||
XPUSHs(sv_2mortal(newSVpv(ev_name, 0)));
|
||||
XPUSHs(sv_2mortal(newSVpv(field_name, 0)));
|
||||
XPUSHs(sv_2mortal(newSVpv(delim, 0)));
|
||||
|
||||
PUTBACK;
|
||||
if (get_cv("main::define_flag_field", 0))
|
||||
call_pv("main::define_flag_field", G_SCALAR);
|
||||
SPAGAIN;
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
static void define_event_symbols(struct tep_event *event,
|
||||
const char *ev_name,
|
||||
struct tep_print_arg *args)
|
||||
{
|
||||
if (args == NULL)
|
||||
return;
|
||||
|
||||
switch (args->type) {
|
||||
case TEP_PRINT_NULL:
|
||||
break;
|
||||
case TEP_PRINT_ATOM:
|
||||
define_flag_value(ev_name, cur_field_name, "0",
|
||||
args->atom.atom);
|
||||
zero_flag_atom = 0;
|
||||
break;
|
||||
case TEP_PRINT_FIELD:
|
||||
free(cur_field_name);
|
||||
cur_field_name = strdup(args->field.name);
|
||||
break;
|
||||
case TEP_PRINT_FLAGS:
|
||||
define_event_symbols(event, ev_name, args->flags.field);
|
||||
define_flag_field(ev_name, cur_field_name, args->flags.delim);
|
||||
define_flag_values(args->flags.flags, ev_name, cur_field_name);
|
||||
break;
|
||||
case TEP_PRINT_SYMBOL:
|
||||
define_event_symbols(event, ev_name, args->symbol.field);
|
||||
define_symbolic_field(ev_name, cur_field_name);
|
||||
define_symbolic_values(args->symbol.symbols, ev_name,
|
||||
cur_field_name);
|
||||
break;
|
||||
case TEP_PRINT_HEX:
|
||||
case TEP_PRINT_HEX_STR:
|
||||
define_event_symbols(event, ev_name, args->hex.field);
|
||||
define_event_symbols(event, ev_name, args->hex.size);
|
||||
break;
|
||||
case TEP_PRINT_INT_ARRAY:
|
||||
define_event_symbols(event, ev_name, args->int_array.field);
|
||||
define_event_symbols(event, ev_name, args->int_array.count);
|
||||
define_event_symbols(event, ev_name, args->int_array.el_size);
|
||||
break;
|
||||
case TEP_PRINT_BSTRING:
|
||||
case TEP_PRINT_DYNAMIC_ARRAY:
|
||||
case TEP_PRINT_DYNAMIC_ARRAY_LEN:
|
||||
case TEP_PRINT_STRING:
|
||||
case TEP_PRINT_BITMASK:
|
||||
break;
|
||||
case TEP_PRINT_TYPE:
|
||||
define_event_symbols(event, ev_name, args->typecast.item);
|
||||
break;
|
||||
case TEP_PRINT_OP:
|
||||
if (strcmp(args->op.op, ":") == 0)
|
||||
zero_flag_atom = 1;
|
||||
define_event_symbols(event, ev_name, args->op.left);
|
||||
define_event_symbols(event, ev_name, args->op.right);
|
||||
break;
|
||||
case TEP_PRINT_FUNC:
|
||||
default:
|
||||
pr_err("Unsupported print arg type\n");
|
||||
/* we should warn... */
|
||||
return;
|
||||
}
|
||||
|
||||
if (args->next)
|
||||
define_event_symbols(event, ev_name, args->next);
|
||||
}
|
||||
|
||||
static SV *perl_process_callchain(struct perf_sample *sample,
|
||||
struct addr_location *al)
|
||||
{
|
||||
struct callchain_cursor *cursor;
|
||||
AV *list;
|
||||
|
||||
list = newAV();
|
||||
if (!list)
|
||||
goto exit;
|
||||
|
||||
if (!symbol_conf.use_callchain || !sample->callchain)
|
||||
goto exit;
|
||||
|
||||
cursor = get_tls_callchain_cursor();
|
||||
|
||||
if (thread__resolve_callchain(al->thread, cursor,
|
||||
sample, NULL, NULL, scripting_max_stack) != 0) {
|
||||
pr_err("Failed to resolve callchain. Skipping\n");
|
||||
goto exit;
|
||||
}
|
||||
callchain_cursor_commit(cursor);
|
||||
|
||||
|
||||
while (1) {
|
||||
HV *elem;
|
||||
struct callchain_cursor_node *node;
|
||||
node = callchain_cursor_current(cursor);
|
||||
if (!node)
|
||||
break;
|
||||
|
||||
elem = newHV();
|
||||
if (!elem)
|
||||
goto exit;
|
||||
|
||||
if (!hv_stores(elem, "ip", newSVuv(node->ip))) {
|
||||
hv_undef(elem);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (node->ms.sym) {
|
||||
HV *sym = newHV();
|
||||
if (!sym) {
|
||||
hv_undef(elem);
|
||||
goto exit;
|
||||
}
|
||||
if (!hv_stores(sym, "start", newSVuv(node->ms.sym->start)) ||
|
||||
!hv_stores(sym, "end", newSVuv(node->ms.sym->end)) ||
|
||||
!hv_stores(sym, "binding", newSVuv(symbol__binding(node->ms.sym))) ||
|
||||
!hv_stores(sym, "name", newSVpvn(node->ms.sym->name,
|
||||
node->ms.sym->namelen)) ||
|
||||
!hv_stores(elem, "sym", newRV_noinc((SV*)sym))) {
|
||||
hv_undef(sym);
|
||||
hv_undef(elem);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (node->ms.map) {
|
||||
struct map *map = node->ms.map;
|
||||
struct dso *dso = map ? map__dso(map) : NULL;
|
||||
const char *dsoname = "[unknown]";
|
||||
|
||||
if (dso) {
|
||||
if (symbol_conf.show_kernel_path && dso__long_name(dso))
|
||||
dsoname = dso__long_name(dso);
|
||||
else
|
||||
dsoname = dso__name(dso);
|
||||
}
|
||||
if (!hv_stores(elem, "dso", newSVpv(dsoname,0))) {
|
||||
hv_undef(elem);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
callchain_cursor_advance(cursor);
|
||||
av_push(list, newRV_noinc((SV*)elem));
|
||||
}
|
||||
|
||||
exit:
|
||||
return newRV_noinc((SV*)list);
|
||||
}
|
||||
|
||||
static void perl_process_tracepoint(struct perf_sample *sample,
|
||||
struct addr_location *al)
|
||||
{
|
||||
struct thread *thread = al->thread;
|
||||
struct tep_event *event;
|
||||
struct tep_format_field *field;
|
||||
static char handler[256];
|
||||
unsigned long long val;
|
||||
unsigned long s, ns;
|
||||
int pid;
|
||||
int cpu = sample->cpu;
|
||||
void *data = sample->raw_data;
|
||||
unsigned long long nsecs = sample->time;
|
||||
const char *comm = thread__comm_str(thread);
|
||||
DECLARE_BITMAP(events_defined, TRACE_EVENT_TYPE_MAX);
|
||||
struct evsel *evsel = sample->evsel;
|
||||
|
||||
bitmap_zero(events_defined, TRACE_EVENT_TYPE_MAX);
|
||||
dSP;
|
||||
|
||||
if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
|
||||
return;
|
||||
|
||||
event = evsel__tp_format(evsel);
|
||||
if (!event) {
|
||||
pr_debug("ug! no event found for type %" PRIu64, (u64)evsel->core.attr.config);
|
||||
return;
|
||||
}
|
||||
|
||||
pid = raw_field_value(event, "common_pid", data);
|
||||
|
||||
sprintf(handler, "%s::%s", event->system, event->name);
|
||||
|
||||
if (!__test_and_set_bit(event->id, events_defined))
|
||||
define_event_symbols(event, handler, event->print_fmt.args);
|
||||
|
||||
s = nsecs / NSEC_PER_SEC;
|
||||
ns = nsecs - s * NSEC_PER_SEC;
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
PUSHMARK(SP);
|
||||
|
||||
XPUSHs(sv_2mortal(newSVpv(handler, 0)));
|
||||
XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context))));
|
||||
XPUSHs(sv_2mortal(newSVuv(cpu)));
|
||||
XPUSHs(sv_2mortal(newSVuv(s)));
|
||||
XPUSHs(sv_2mortal(newSVuv(ns)));
|
||||
XPUSHs(sv_2mortal(newSViv(pid)));
|
||||
XPUSHs(sv_2mortal(newSVpv(comm, 0)));
|
||||
XPUSHs(sv_2mortal(perl_process_callchain(sample, al)));
|
||||
|
||||
/* common fields other than pid can be accessed via xsub fns */
|
||||
|
||||
for (field = event->format.fields; field; field = field->next) {
|
||||
if (field->flags & TEP_FIELD_IS_STRING) {
|
||||
int offset;
|
||||
if (field->flags & TEP_FIELD_IS_DYNAMIC) {
|
||||
offset = *(int *)(data + field->offset);
|
||||
offset &= 0xffff;
|
||||
if (tep_field_is_relative(field->flags))
|
||||
offset += field->offset + field->size;
|
||||
} else
|
||||
offset = field->offset;
|
||||
XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));
|
||||
} else { /* FIELD_IS_NUMERIC */
|
||||
val = read_size(event, data + field->offset,
|
||||
field->size);
|
||||
if (field->flags & TEP_FIELD_IS_SIGNED) {
|
||||
XPUSHs(sv_2mortal(newSViv(val)));
|
||||
} else {
|
||||
XPUSHs(sv_2mortal(newSVuv(val)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PUTBACK;
|
||||
|
||||
if (get_cv(handler, 0))
|
||||
call_pv(handler, G_SCALAR);
|
||||
else if (get_cv("main::trace_unhandled", 0)) {
|
||||
XPUSHs(sv_2mortal(newSVpv(handler, 0)));
|
||||
XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context))));
|
||||
XPUSHs(sv_2mortal(newSVuv(cpu)));
|
||||
XPUSHs(sv_2mortal(newSVuv(nsecs)));
|
||||
XPUSHs(sv_2mortal(newSViv(pid)));
|
||||
XPUSHs(sv_2mortal(newSVpv(comm, 0)));
|
||||
XPUSHs(sv_2mortal(perl_process_callchain(sample, al)));
|
||||
call_pv("main::trace_unhandled", G_SCALAR);
|
||||
}
|
||||
SPAGAIN;
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
static void perl_process_event_generic(union perf_event *event, struct perf_sample *sample)
|
||||
{
|
||||
dSP;
|
||||
|
||||
if (!get_cv("process_event", 0))
|
||||
return;
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
PUSHMARK(SP);
|
||||
XPUSHs(sv_2mortal(newSVpvn((const char *)event, event->header.size)));
|
||||
XPUSHs(sv_2mortal(newSVpvn((const char *)&sample->evsel->core.attr,
|
||||
sizeof(sample->evsel->core.attr))));
|
||||
XPUSHs(sv_2mortal(newSVpvn((const char *)sample, sizeof(*sample))));
|
||||
XPUSHs(sv_2mortal(newSVpvn((const char *)sample->raw_data, sample->raw_size)));
|
||||
PUTBACK;
|
||||
call_pv("process_event", G_SCALAR);
|
||||
SPAGAIN;
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
static void perl_process_event(union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct addr_location *al,
|
||||
struct addr_location *addr_al)
|
||||
{
|
||||
scripting_context__update(scripting_context, event, sample, al, addr_al);
|
||||
perl_process_tracepoint(sample, al);
|
||||
perl_process_event_generic(event, sample);
|
||||
}
|
||||
|
||||
static void run_start_sub(void)
|
||||
{
|
||||
dSP; /* access to Perl stack */
|
||||
PUSHMARK(SP);
|
||||
|
||||
if (get_cv("main::trace_begin", 0))
|
||||
call_pv("main::trace_begin", G_DISCARD | G_NOARGS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start trace script
|
||||
*/
|
||||
static int perl_start_script(const char *script, int argc, const char **argv,
|
||||
struct perf_session *session)
|
||||
{
|
||||
const char **command_line;
|
||||
int i, err = 0;
|
||||
|
||||
scripting_context->session = session;
|
||||
|
||||
command_line = malloc((argc + 2) * sizeof(const char *));
|
||||
if (!command_line)
|
||||
return -ENOMEM;
|
||||
|
||||
command_line[0] = "";
|
||||
command_line[1] = script;
|
||||
for (i = 2; i < argc + 2; i++)
|
||||
command_line[i] = argv[i - 2];
|
||||
|
||||
my_perl = perl_alloc();
|
||||
perl_construct(my_perl);
|
||||
|
||||
if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line,
|
||||
(char **)NULL)) {
|
||||
err = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (perl_run(my_perl)) {
|
||||
err = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (SvTRUE(ERRSV)) {
|
||||
err = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
run_start_sub();
|
||||
|
||||
free(command_line);
|
||||
return 0;
|
||||
error:
|
||||
perl_free(my_perl);
|
||||
free(command_line);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int perl_flush_script(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop trace script
|
||||
*/
|
||||
static int perl_stop_script(void)
|
||||
{
|
||||
dSP; /* access to Perl stack */
|
||||
PUSHMARK(SP);
|
||||
|
||||
if (get_cv("main::trace_end", 0))
|
||||
call_pv("main::trace_end", G_DISCARD | G_NOARGS);
|
||||
|
||||
perl_destruct(my_perl);
|
||||
perl_free(my_perl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perl_generate_script(struct tep_handle *pevent, const char *outfile)
|
||||
{
|
||||
int i, not_first, count, nr_events;
|
||||
struct tep_event **all_events;
|
||||
struct tep_event *event = NULL;
|
||||
struct tep_format_field *f;
|
||||
char fname[PATH_MAX];
|
||||
FILE *ofp;
|
||||
|
||||
sprintf(fname, "%s.pl", outfile);
|
||||
ofp = fopen(fname, "w");
|
||||
if (ofp == NULL) {
|
||||
fprintf(stderr, "couldn't open %s\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(ofp, "# perf script event handlers, "
|
||||
"generated by perf script -g perl\n");
|
||||
|
||||
fprintf(ofp, "# Licensed under the terms of the GNU GPL"
|
||||
" License version 2\n\n");
|
||||
|
||||
fprintf(ofp, "# The common_* event handler fields are the most useful "
|
||||
"fields common to\n");
|
||||
|
||||
fprintf(ofp, "# all events. They don't necessarily correspond to "
|
||||
"the 'common_*' fields\n");
|
||||
|
||||
fprintf(ofp, "# in the format files. Those fields not available as "
|
||||
"handler params can\n");
|
||||
|
||||
fprintf(ofp, "# be retrieved using Perl functions of the form "
|
||||
"common_*($context).\n");
|
||||
|
||||
fprintf(ofp, "# See Context.pm for the list of available "
|
||||
"functions.\n\n");
|
||||
|
||||
fprintf(ofp, "use lib \"$ENV{'PERF_EXEC_PATH'}/scripts/perl/"
|
||||
"Perf-Trace-Util/lib\";\n");
|
||||
|
||||
fprintf(ofp, "use lib \"./Perf-Trace-Util/lib\";\n");
|
||||
fprintf(ofp, "use Perf::Trace::Core;\n");
|
||||
fprintf(ofp, "use Perf::Trace::Context;\n");
|
||||
fprintf(ofp, "use Perf::Trace::Util;\n\n");
|
||||
|
||||
fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n");
|
||||
fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n");
|
||||
|
||||
|
||||
fprintf(ofp, "\n\
|
||||
sub print_backtrace\n\
|
||||
{\n\
|
||||
my $callchain = shift;\n\
|
||||
for my $node (@$callchain)\n\
|
||||
{\n\
|
||||
if(exists $node->{sym})\n\
|
||||
{\n\
|
||||
printf( \"\\t[\\%%x] \\%%s\\n\", $node->{ip}, $node->{sym}{name});\n\
|
||||
}\n\
|
||||
else\n\
|
||||
{\n\
|
||||
printf( \"\\t[\\%%x]\\n\", $node{ip});\n\
|
||||
}\n\
|
||||
}\n\
|
||||
}\n\n\
|
||||
");
|
||||
|
||||
nr_events = tep_get_events_count(pevent);
|
||||
all_events = tep_list_events(pevent, TEP_EVENT_SORT_ID);
|
||||
|
||||
for (i = 0; all_events && i < nr_events; i++) {
|
||||
event = all_events[i];
|
||||
fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name);
|
||||
fprintf(ofp, "\tmy (");
|
||||
|
||||
fprintf(ofp, "$event_name, ");
|
||||
fprintf(ofp, "$context, ");
|
||||
fprintf(ofp, "$common_cpu, ");
|
||||
fprintf(ofp, "$common_secs, ");
|
||||
fprintf(ofp, "$common_nsecs,\n");
|
||||
fprintf(ofp, "\t $common_pid, ");
|
||||
fprintf(ofp, "$common_comm, ");
|
||||
fprintf(ofp, "$common_callchain,\n\t ");
|
||||
|
||||
not_first = 0;
|
||||
count = 0;
|
||||
|
||||
for (f = event->format.fields; f; f = f->next) {
|
||||
if (not_first++)
|
||||
fprintf(ofp, ", ");
|
||||
if (++count % 5 == 0)
|
||||
fprintf(ofp, "\n\t ");
|
||||
|
||||
fprintf(ofp, "$%s", f->name);
|
||||
}
|
||||
fprintf(ofp, ") = @_;\n\n");
|
||||
|
||||
fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
|
||||
"$common_secs, $common_nsecs,\n\t "
|
||||
"$common_pid, $common_comm, $common_callchain);\n\n");
|
||||
|
||||
fprintf(ofp, "\tprintf(\"");
|
||||
|
||||
not_first = 0;
|
||||
count = 0;
|
||||
|
||||
for (f = event->format.fields; f; f = f->next) {
|
||||
if (not_first++)
|
||||
fprintf(ofp, ", ");
|
||||
if (count && count % 4 == 0) {
|
||||
fprintf(ofp, "\".\n\t \"");
|
||||
}
|
||||
count++;
|
||||
|
||||
fprintf(ofp, "%s=", f->name);
|
||||
if (f->flags & TEP_FIELD_IS_STRING ||
|
||||
f->flags & TEP_FIELD_IS_FLAG ||
|
||||
f->flags & TEP_FIELD_IS_SYMBOLIC)
|
||||
fprintf(ofp, "%%s");
|
||||
else if (f->flags & TEP_FIELD_IS_SIGNED)
|
||||
fprintf(ofp, "%%d");
|
||||
else
|
||||
fprintf(ofp, "%%u");
|
||||
}
|
||||
|
||||
fprintf(ofp, "\\n\",\n\t ");
|
||||
|
||||
not_first = 0;
|
||||
count = 0;
|
||||
|
||||
for (f = event->format.fields; f; f = f->next) {
|
||||
if (not_first++)
|
||||
fprintf(ofp, ", ");
|
||||
|
||||
if (++count % 5 == 0)
|
||||
fprintf(ofp, "\n\t ");
|
||||
|
||||
if (f->flags & TEP_FIELD_IS_FLAG) {
|
||||
if ((count - 1) % 5 != 0) {
|
||||
fprintf(ofp, "\n\t ");
|
||||
count = 4;
|
||||
}
|
||||
fprintf(ofp, "flag_str(\"");
|
||||
fprintf(ofp, "%s::%s\", ", event->system,
|
||||
event->name);
|
||||
fprintf(ofp, "\"%s\", $%s)", f->name,
|
||||
f->name);
|
||||
} else if (f->flags & TEP_FIELD_IS_SYMBOLIC) {
|
||||
if ((count - 1) % 5 != 0) {
|
||||
fprintf(ofp, "\n\t ");
|
||||
count = 4;
|
||||
}
|
||||
fprintf(ofp, "symbol_str(\"");
|
||||
fprintf(ofp, "%s::%s\", ", event->system,
|
||||
event->name);
|
||||
fprintf(ofp, "\"%s\", $%s)", f->name,
|
||||
f->name);
|
||||
} else
|
||||
fprintf(ofp, "$%s", f->name);
|
||||
}
|
||||
|
||||
fprintf(ofp, ");\n\n");
|
||||
|
||||
fprintf(ofp, "\tprint_backtrace($common_callchain);\n");
|
||||
|
||||
fprintf(ofp, "}\n\n");
|
||||
}
|
||||
|
||||
fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, "
|
||||
"$common_cpu, $common_secs, $common_nsecs,\n\t "
|
||||
"$common_pid, $common_comm, $common_callchain) = @_;\n\n");
|
||||
|
||||
fprintf(ofp, "\tprint_header($event_name, $common_cpu, "
|
||||
"$common_secs, $common_nsecs,\n\t $common_pid, "
|
||||
"$common_comm, $common_callchain);\n");
|
||||
fprintf(ofp, "\tprint_backtrace($common_callchain);\n");
|
||||
fprintf(ofp, "}\n\n");
|
||||
|
||||
fprintf(ofp, "sub print_header\n{\n"
|
||||
"\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n"
|
||||
"\tprintf(\"%%-20s %%5u %%05u.%%09u %%8u %%-20s \",\n\t "
|
||||
"$event_name, $cpu, $secs, $nsecs, $pid, $comm);\n}\n");
|
||||
|
||||
fprintf(ofp,
|
||||
"\n# Packed byte string args of process_event():\n"
|
||||
"#\n"
|
||||
"# $event:\tunion perf_event\tutil/event.h\n"
|
||||
"# $attr:\tstruct perf_event_attr\tlinux/perf_event.h\n"
|
||||
"# $sample:\tstruct perf_sample\tutil/event.h\n"
|
||||
"# $raw_data:\tperf_sample->raw_data\tutil/event.h\n"
|
||||
"\n"
|
||||
"sub process_event\n"
|
||||
"{\n"
|
||||
"\tmy ($event, $attr, $sample, $raw_data) = @_;\n"
|
||||
"\n"
|
||||
"\tmy @event\t= unpack(\"LSS\", $event);\n"
|
||||
"\tmy @attr\t= unpack(\"LLQQQQQLLQQ\", $attr);\n"
|
||||
"\tmy @sample\t= unpack(\"QLLQQQQQLL\", $sample);\n"
|
||||
"\tmy @raw_data\t= unpack(\"C*\", $raw_data);\n"
|
||||
"\n"
|
||||
"\tuse Data::Dumper;\n"
|
||||
"\tprint Dumper \\@event, \\@attr, \\@sample, \\@raw_data;\n"
|
||||
"}\n");
|
||||
|
||||
fclose(ofp);
|
||||
|
||||
fprintf(stderr, "generated Perl script: %s\n", fname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct scripting_ops perl_scripting_ops = {
|
||||
.name = "Perl",
|
||||
.dirname = "perl",
|
||||
.start_script = perl_start_script,
|
||||
.flush_script = perl_flush_script,
|
||||
.stop_script = perl_stop_script,
|
||||
.process_event = perl_process_event,
|
||||
.generate_script = perl_generate_script,
|
||||
};
|
||||
|
|
@ -14,71 +14,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <event-parse.h>
|
||||
|
||||
static int get_common_field(struct scripting_context *context,
|
||||
int *offset, int *size, const char *type)
|
||||
{
|
||||
struct tep_handle *pevent = context->pevent;
|
||||
struct tep_event *event;
|
||||
struct tep_format_field *field;
|
||||
|
||||
if (!*size) {
|
||||
|
||||
event = tep_get_first_event(pevent);
|
||||
if (!event)
|
||||
return 0;
|
||||
|
||||
field = tep_find_common_field(event, type);
|
||||
if (!field)
|
||||
return 0;
|
||||
*offset = field->offset;
|
||||
*size = field->size;
|
||||
}
|
||||
|
||||
return tep_read_number(pevent, context->event_data + *offset, *size);
|
||||
}
|
||||
|
||||
int common_lock_depth(struct scripting_context *context)
|
||||
{
|
||||
static int offset;
|
||||
static int size;
|
||||
int ret;
|
||||
|
||||
ret = get_common_field(context, &size, &offset,
|
||||
"common_lock_depth");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int common_flags(struct scripting_context *context)
|
||||
{
|
||||
static int offset;
|
||||
static int size;
|
||||
int ret;
|
||||
|
||||
ret = get_common_field(context, &size, &offset,
|
||||
"common_flags");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int common_pc(struct scripting_context *context)
|
||||
{
|
||||
static int offset;
|
||||
static int size;
|
||||
int ret;
|
||||
|
||||
ret = get_common_field(context, &size, &offset,
|
||||
"common_preempt_count");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned long long
|
||||
raw_field_value(struct tep_event *event, const char *name, void *data)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,398 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* trace-event-scripting. Scripting engine common and initialization code.
|
||||
*
|
||||
* Copyright (C) 2009-2010 Tom Zanussi <tzanussi@gmail.com>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
#include <event-parse.h>
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
#include "event.h"
|
||||
#include "trace-event.h"
|
||||
#include "evsel.h"
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/zalloc.h>
|
||||
#include "util/sample.h"
|
||||
|
||||
unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;
|
||||
|
||||
struct scripting_context *scripting_context;
|
||||
|
||||
struct script_spec {
|
||||
struct list_head node;
|
||||
struct scripting_ops *ops;
|
||||
char spec[];
|
||||
};
|
||||
|
||||
static LIST_HEAD(script_specs);
|
||||
|
||||
static struct script_spec *script_spec__new(const char *spec,
|
||||
struct scripting_ops *ops)
|
||||
{
|
||||
struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1);
|
||||
|
||||
if (s != NULL) {
|
||||
strcpy(s->spec, spec);
|
||||
s->ops = ops;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void script_spec__add(struct script_spec *s)
|
||||
{
|
||||
list_add_tail(&s->node, &script_specs);
|
||||
}
|
||||
|
||||
static struct script_spec *script_spec__find(const char *spec)
|
||||
{
|
||||
struct script_spec *s;
|
||||
|
||||
list_for_each_entry(s, &script_specs, node)
|
||||
if (strcasecmp(s->spec, spec) == 0)
|
||||
return s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int script_spec_register(const char *spec, struct scripting_ops *ops)
|
||||
{
|
||||
struct script_spec *s;
|
||||
|
||||
s = script_spec__find(spec);
|
||||
if (s)
|
||||
return -1;
|
||||
|
||||
s = script_spec__new(spec, ops);
|
||||
if (!s)
|
||||
return -1;
|
||||
|
||||
script_spec__add(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct scripting_ops *script_spec__lookup(const char *spec)
|
||||
{
|
||||
struct script_spec *s = script_spec__find(spec);
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
return s->ops;
|
||||
}
|
||||
|
||||
int script_spec__for_each(int (*cb)(struct scripting_ops *ops, const char *spec))
|
||||
{
|
||||
struct script_spec *s;
|
||||
int ret = 0;
|
||||
|
||||
list_for_each_entry(s, &script_specs, node) {
|
||||
ret = cb(s->ops, s->spec);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void scripting_context__update(struct scripting_context *c,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct addr_location *al,
|
||||
struct addr_location *addr_al)
|
||||
{
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
const struct tep_event *tp_format = evsel__tp_format(sample->evsel);
|
||||
|
||||
c->pevent = tp_format ? tp_format->tep : NULL;
|
||||
#else
|
||||
c->pevent = NULL;
|
||||
#endif
|
||||
c->event_data = sample->raw_data;
|
||||
c->event = event;
|
||||
c->sample = sample;
|
||||
c->al = al;
|
||||
c->addr_al = addr_al;
|
||||
}
|
||||
|
||||
static int flush_script_unsupported(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stop_script_unsupported(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void process_event_unsupported(union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct addr_location *al __maybe_unused,
|
||||
struct addr_location *addr_al __maybe_unused)
|
||||
{
|
||||
}
|
||||
|
||||
static void print_python_unsupported_msg(void)
|
||||
{
|
||||
fprintf(stderr, "Python scripting not supported."
|
||||
" Install libpython and rebuild perf to enable it.\n"
|
||||
"For example:\n # apt-get install python-dev (ubuntu)"
|
||||
"\n # yum install python-devel (Fedora)"
|
||||
"\n etc.\n");
|
||||
}
|
||||
|
||||
static int python_start_script_unsupported(const char *script __maybe_unused,
|
||||
int argc __maybe_unused,
|
||||
const char **argv __maybe_unused,
|
||||
struct perf_session *session __maybe_unused)
|
||||
{
|
||||
print_python_unsupported_msg();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int python_generate_script_unsupported(struct tep_handle *pevent
|
||||
__maybe_unused,
|
||||
const char *outfile
|
||||
__maybe_unused)
|
||||
{
|
||||
print_python_unsupported_msg();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct scripting_ops python_scripting_unsupported_ops = {
|
||||
.name = "Python",
|
||||
.dirname = "python",
|
||||
.start_script = python_start_script_unsupported,
|
||||
.flush_script = flush_script_unsupported,
|
||||
.stop_script = stop_script_unsupported,
|
||||
.process_event = process_event_unsupported,
|
||||
.generate_script = python_generate_script_unsupported,
|
||||
};
|
||||
|
||||
static void register_python_scripting(struct scripting_ops *scripting_ops)
|
||||
{
|
||||
if (scripting_context == NULL)
|
||||
scripting_context = malloc(sizeof(*scripting_context));
|
||||
|
||||
if (scripting_context == NULL ||
|
||||
script_spec_register("Python", scripting_ops) ||
|
||||
script_spec_register("py", scripting_ops)) {
|
||||
pr_err("Error registering Python script extension: disabling it\n");
|
||||
zfree(&scripting_context);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_python_scripting(void)
|
||||
{
|
||||
register_python_scripting(&python_scripting_unsupported_ops);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
static void print_perl_unsupported_msg(void)
|
||||
{
|
||||
fprintf(stderr, "Perl scripting not supported."
|
||||
" Install libperl and rebuild perf to enable it.\n"
|
||||
"For example:\n # apt-get install libperl-dev (ubuntu)"
|
||||
"\n # yum install 'perl(ExtUtils::Embed)' (Fedora)"
|
||||
"\n etc.\n");
|
||||
}
|
||||
|
||||
static int perl_start_script_unsupported(const char *script __maybe_unused,
|
||||
int argc __maybe_unused,
|
||||
const char **argv __maybe_unused,
|
||||
struct perf_session *session __maybe_unused)
|
||||
{
|
||||
print_perl_unsupported_msg();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int perl_generate_script_unsupported(struct tep_handle *pevent
|
||||
__maybe_unused,
|
||||
const char *outfile __maybe_unused)
|
||||
{
|
||||
print_perl_unsupported_msg();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct scripting_ops perl_scripting_unsupported_ops = {
|
||||
.name = "Perl",
|
||||
.dirname = "perl",
|
||||
.start_script = perl_start_script_unsupported,
|
||||
.flush_script = flush_script_unsupported,
|
||||
.stop_script = stop_script_unsupported,
|
||||
.process_event = process_event_unsupported,
|
||||
.generate_script = perl_generate_script_unsupported,
|
||||
};
|
||||
|
||||
static void register_perl_scripting(struct scripting_ops *scripting_ops)
|
||||
{
|
||||
if (scripting_context == NULL)
|
||||
scripting_context = malloc(sizeof(*scripting_context));
|
||||
|
||||
if (scripting_context == NULL ||
|
||||
script_spec_register("Perl", scripting_ops) ||
|
||||
script_spec_register("pl", scripting_ops)) {
|
||||
pr_err("Error registering Perl script extension: disabling it\n");
|
||||
zfree(&scripting_context);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_LIBPERL_SUPPORT
|
||||
void setup_perl_scripting(void)
|
||||
{
|
||||
register_perl_scripting(&perl_scripting_unsupported_ops);
|
||||
}
|
||||
#else
|
||||
extern struct scripting_ops perl_scripting_ops;
|
||||
|
||||
void setup_perl_scripting(void)
|
||||
{
|
||||
register_perl_scripting(&perl_scripting_ops);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const struct {
|
||||
u32 flags;
|
||||
const char *name;
|
||||
} sample_flags[] = {
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "jcc"},
|
||||
{PERF_IP_FLAG_BRANCH, "jmp"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, "int"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, "iret"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, "syscall"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, "sysret"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "async"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | PERF_IP_FLAG_INTERRUPT,
|
||||
"hw int"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "tx abrt"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "tr strt"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "tr end"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMENTRY, "vmentry"},
|
||||
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMEXIT, "vmexit"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static const struct {
|
||||
u32 flags;
|
||||
const char *name;
|
||||
} branch_events[] = {
|
||||
{PERF_IP_FLAG_BRANCH_MISS, "miss"},
|
||||
{PERF_IP_FLAG_NOT_TAKEN, "not_taken"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static int sample_flags_to_name(u32 flags, char *str, size_t size)
|
||||
{
|
||||
int i;
|
||||
const char *prefix;
|
||||
int pos = 0, ret, ev_idx = 0;
|
||||
u32 xf = flags & PERF_ADDITIONAL_STATE_MASK;
|
||||
u32 types, events;
|
||||
char xs[16] = { 0 };
|
||||
|
||||
/* Clear additional state bits */
|
||||
flags &= ~PERF_ADDITIONAL_STATE_MASK;
|
||||
|
||||
if (flags & PERF_IP_FLAG_TRACE_BEGIN)
|
||||
prefix = "tr strt ";
|
||||
else if (flags & PERF_IP_FLAG_TRACE_END)
|
||||
prefix = "tr end ";
|
||||
else
|
||||
prefix = "";
|
||||
|
||||
ret = snprintf(str + pos, size - pos, "%s", prefix);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pos += ret;
|
||||
|
||||
flags &= ~(PERF_IP_FLAG_TRACE_BEGIN | PERF_IP_FLAG_TRACE_END);
|
||||
|
||||
types = flags & ~PERF_IP_FLAG_BRANCH_EVENT_MASK;
|
||||
for (i = 0; sample_flags[i].name; i++) {
|
||||
if (sample_flags[i].flags != types)
|
||||
continue;
|
||||
|
||||
ret = snprintf(str + pos, size - pos, "%s", sample_flags[i].name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pos += ret;
|
||||
break;
|
||||
}
|
||||
|
||||
events = flags & PERF_IP_FLAG_BRANCH_EVENT_MASK;
|
||||
for (i = 0; branch_events[i].name; i++) {
|
||||
if (!(branch_events[i].flags & events))
|
||||
continue;
|
||||
|
||||
ret = snprintf(str + pos, size - pos, !ev_idx ? "/%s" : ",%s",
|
||||
branch_events[i].name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pos += ret;
|
||||
ev_idx++;
|
||||
}
|
||||
|
||||
/* Add an end character '/' for events */
|
||||
if (ev_idx) {
|
||||
ret = snprintf(str + pos, size - pos, "/");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
if (!xf)
|
||||
return pos;
|
||||
|
||||
snprintf(xs, sizeof(xs), "(%s%s%s)",
|
||||
flags & PERF_IP_FLAG_IN_TX ? "x" : "",
|
||||
flags & PERF_IP_FLAG_INTR_DISABLE ? "D" : "",
|
||||
flags & PERF_IP_FLAG_INTR_TOGGLE ? "t" : "");
|
||||
|
||||
/* Right align the string if its length is less than the limit */
|
||||
if ((pos + strlen(xs)) < SAMPLE_FLAGS_STR_ALIGNED_SIZE)
|
||||
ret = snprintf(str + pos, size - pos, "%*s",
|
||||
(int)(SAMPLE_FLAGS_STR_ALIGNED_SIZE - ret), xs);
|
||||
else
|
||||
ret = snprintf(str + pos, size - pos, " %s", xs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return pos + ret;
|
||||
}
|
||||
|
||||
int perf_sample__sprintf_flags(u32 flags, char *str, size_t sz)
|
||||
{
|
||||
const char *chars = PERF_IP_FLAG_CHARS;
|
||||
const size_t n = strlen(PERF_IP_FLAG_CHARS);
|
||||
size_t i, pos = 0;
|
||||
int ret;
|
||||
|
||||
ret = sample_flags_to_name(flags, str, sz);
|
||||
if (ret > 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < n; i++, flags >>= 1) {
|
||||
if ((flags & 1) && pos < sz)
|
||||
str[pos++] = chars[i];
|
||||
}
|
||||
for (; i < 32; i++, flags >>= 1) {
|
||||
if ((flags & 1) && pos < sz)
|
||||
str[pos++] = '?';
|
||||
}
|
||||
if (pos < sz)
|
||||
str[pos] = 0;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
|
@ -7,15 +7,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct evlist;
|
||||
struct machine;
|
||||
struct perf_sample;
|
||||
union perf_event;
|
||||
struct perf_tool;
|
||||
struct thread;
|
||||
struct tep_plugin_list;
|
||||
struct evsel;
|
||||
struct tep_format_field;
|
||||
struct tep_plugin_list;
|
||||
|
||||
struct trace_event {
|
||||
struct tep_handle *pevent;
|
||||
|
|
@ -80,70 +74,6 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
|||
int fd, bool temp);
|
||||
int tracing_data_put(struct tracing_data *tdata);
|
||||
|
||||
|
||||
struct addr_location;
|
||||
|
||||
struct perf_session;
|
||||
struct perf_stat_config;
|
||||
|
||||
struct scripting_ops {
|
||||
const char *name;
|
||||
const char *dirname; /* For script path .../scripts/<dirname>/... */
|
||||
int (*start_script)(const char *script, int argc, const char **argv,
|
||||
struct perf_session *session);
|
||||
int (*flush_script) (void);
|
||||
int (*stop_script) (void);
|
||||
void (*process_event) (union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct addr_location *al,
|
||||
struct addr_location *addr_al);
|
||||
void (*process_switch)(union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
void (*process_auxtrace_error)(struct perf_session *session,
|
||||
union perf_event *event);
|
||||
void (*process_stat)(struct perf_stat_config *config,
|
||||
struct evsel *evsel, u64 tstamp);
|
||||
void (*process_stat_interval)(u64 tstamp);
|
||||
void (*process_throttle)(union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int (*generate_script) (struct tep_handle *pevent, const char *outfile);
|
||||
};
|
||||
|
||||
extern unsigned int scripting_max_stack;
|
||||
|
||||
struct scripting_ops *script_spec__lookup(const char *spec);
|
||||
int script_spec__for_each(int (*cb)(struct scripting_ops *ops, const char *spec));
|
||||
|
||||
void setup_perl_scripting(void);
|
||||
void setup_python_scripting(void);
|
||||
|
||||
struct scripting_context {
|
||||
struct tep_handle *pevent;
|
||||
void *event_data;
|
||||
union perf_event *event;
|
||||
struct perf_sample *sample;
|
||||
struct addr_location *al;
|
||||
struct addr_location *addr_al;
|
||||
struct perf_session *session;
|
||||
};
|
||||
|
||||
void scripting_context__update(struct scripting_context *scripting_context,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct addr_location *al,
|
||||
struct addr_location *addr_al);
|
||||
|
||||
int common_pc(struct scripting_context *context);
|
||||
int common_flags(struct scripting_context *context);
|
||||
int common_lock_depth(struct scripting_context *context);
|
||||
|
||||
#define SAMPLE_FLAGS_BUF_SIZE 64
|
||||
#define SAMPLE_FLAGS_STR_ALIGNED_SIZE 21
|
||||
|
||||
int perf_sample__sprintf_flags(u32 flags, char *str, size_t sz);
|
||||
|
||||
#if defined(LIBTRACEEVENT_VERSION) && LIBTRACEEVENT_VERSION >= MAKE_LIBTRACEEVENT_VERSION(1, 5, 0)
|
||||
#include <event-parse.h>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user