73 lines
No EOL
7.4 KiB
TeX
73 lines
No EOL
7.4 KiB
TeX
\epigraph{\textit{Never send a human to do a machine's job. }}{--- Agent Smith, \textit{The Matrix}}
|
|
|
|
\noindent Nach der Implementierung der Perl Module mussten die \emph{\glspl{probe}} in den zu untersuchenden Code eingebracht werden. Das EPrints System ist objektorientiert programmiert und verfügt über eine große Anzahl an verwendeten Perl Modulen. In diesen Modulen mussten alle Subroutinen mit OperationEntry- und OperationExitEvents versehen werden.
|
|
Diese Instrumentierung wurde zunächst mit verschiedenen selbstentwickelten statischen Analysen versucht. Diese Versuche sind in \autoref{sec:instrStatisch} beschrieben. Da diese Versuche keine zufriedenstellenden Ergebnisse brachten wurde schließlich das \gls{cpan}-Modul Sub::WrapPackages\footnote{http://search.cpan.org/~dcantrell/Sub-WrapPackages-2.0/lib/Sub/WrapPackages.pm (Zuletzt aufgerufen 2013-03-25)} verwendet, das eine leichtgewichtige Lösung zur Erzeugung von Funktionen um eine existierende Subroutine herum darstellt. Mit diesem Paket wurde dann die Instrumentierung wie in \autoref{sec:instrSubWrap} beschrieben durchgeführt.
|
|
|
|
\section{Statische Integration der Probes}\label{sec:instrStatisch}
|
|
Zunächst wurde versucht die Pakete statisch zu analysieren und nach dieser Analyse die benötigen \emph{\glspl{probe}} in den Quelltext einzubringen. Für OperationEntryEvents funktioniert dieses Verfahren auch zufriedenstellend, indem am Anfang jedes Moduls zunächst ein Kieker Objekt erzeugt wird, dann mittels regulärer Ausdrücke der Beginn von Funktionen erkannt wird und anschließend der folgende Code eingebracht wird.
|
|
|
|
\begin{lstlisting}[caption=Eingebrachtes OperationEntryEvent, label=lst:statischEntry, language=perl]{}
|
|
sub load_source {
|
|
$kieker->EntryEvent( 'load_source', 'EPrints::Citation' );
|
|
\end{lstlisting}
|
|
|
|
\noindent Diese Integration funktioniert weitestgehend automatisch. Um die Zuverlässigkeit zu erhöhen kann hierbei auch das Paket Perl::Tidy eingesetzt werden um Unterschiede in der Formatierung auszugleichen und die regulären Ausdrücke einfacher gestalten zu können.
|
|
|
|
\noindent Ein größeres Problem entstand bei der Erzeugung der nötigen OperationExitEvents. Verfügt die Funktion über ein return Statement kann auch hier in einem naiven Ansatz versucht werden, wie in \autoref{lst:statischExitNaiv} das ExitEvent direkt vor dem return einzufügen.
|
|
|
|
\begin{lstlisting}[caption=Eingebrachtes OperationExitEvent, label=lst:statischExitNaiv, language=perl]{}
|
|
$kieker->ExitEvent( 'load_source', 'EPrints::Citation' );
|
|
return $value
|
|
}
|
|
\end{lstlisting}
|
|
|
|
\noindent Hierbei ist allerdings festzustellen, dass Perl-Funktionen nicht zwingend über ein return Statement verfügen müssen. Sofern die Funktion ohne ein return Statement terminiert, wird der Wert der letzten ausgeführten Operation zurückgegeben. Wie in \autoref{lst:statischNoReturn} können somit z.B. minimale Funktionen realisiert werden, die einen Wert zurückliefern.
|
|
|
|
\begin{lstlisting}[caption=Minimale Funktion ohne return, label=lst:statischNoReturn, language=perl]{}
|
|
sub get_default_charset {
|
|
"utf8"
|
|
}
|
|
\end{lstlisting}
|
|
|
|
\noindent In Kombination mit verschiedenen Verzweigungen oder Schleifen macht dieses Verhalten es schwierig den Wert einer Funktion sicher zu bestimmen und die letzte ausgeführte Operation zu lokalisieren.
|
|
|
|
Weiterhin ist es möglich, dass die Argumente im return Statement über Seiteneffekte verfügen. Hierbei muss dann eine neue Variable erzeugt werden, die den Wert der Anweisung speichert, und die anschließend zurückgegeben werden kann. Diese Konstruktion ist in \autoref{lst:statischNewResult} dargestellt.
|
|
|
|
\begin{lstlisting}[caption=return mit neuer Variablenbindung, label=lst:statischNewResult, language=perl]{}
|
|
my $kieker_return = get_default_charset();
|
|
$kieker->ExitEvent( 'load_source', 'EPrints::Citation' );
|
|
return $kieker_return;
|
|
\end{lstlisting}
|
|
|
|
\noindent Als weiteres Problem stellt sich hierbei allerdings das Typensystem von Perl heraus. So ist es möglich, dass eine Funktion verschiedene Rückgabetypen erzeugt, je nachdem in welchem Kontext sie aufgerufen wird. Somit muss analysiert werden, ob das in \autoref{lst:statischNewResult} erzeugte \emph{kieker\textunderscore return} einen Skalaren Typ oder eine Liste darstellen muss. Die Rückgabe von Hashes wird in Perl nicht unterstützt. Für vordefinierte Funktionen können diese Informationen noch durch ein Parsen der Dokumentation erhalten werden, jedoch ist auch hier schon ein massiver Aufwand von Nöten.
|
|
|
|
Werden nun aber Objektmethoden in dem Return statement aufgerufen, kann nicht mehr aus der Signatur erkannt werden, was für ein Rückgabetyp hier erzeugt wird. Weiterhin existiert das Konstrukt \emph{wantarray} mit dem zur Laufzeit unterschiedliche Typen zurückgegeben werden können, je nachdem in welchem Kontext der Aufruf erfolgt. Somit scheiterte dieser Ansatz der Instrumentierung.
|
|
|
|
\section{Integration mit Sub::WrapPackages}\label{sec:instrSubWrap}
|
|
Als zweiter Ansatz wurde dann die Verwendung eines spezialisierten Paketes aus dem \gls{cpan} gewählt. Das Paket Sub::WrapPackages wurde von David Cantrell entwickelt und dient dazu, gesamte Packages oder auch spezifische Funktionen innerhalb von Packages mit Wrappern zu umschließen und so das Ausführen von beliebigem Code vor und nach der Ausführung ermöglichen.
|
|
|
|
Zur Benutzung des Paketes muss das Paket lediglich vor der Ausführung der Pakete wie folgt eingebunden werden.
|
|
\begin{lstlisting}[caption=Benutzung von Sub::WrapPackages, label=lst:WrapPackages, language=perl, numbers=left]{}
|
|
use Sub::WrapPackages
|
|
packages => [qw(EPrints EPrints::*)],
|
|
pre => sub {
|
|
use Kieker;
|
|
my $kieker = Kieker->new();
|
|
$_[0] =~ s/::/./g;
|
|
$_[0] =~ /^(.*)\..*?$/;
|
|
$kieker->EntryEvent($_[0],$1);
|
|
},
|
|
post => sub {
|
|
use Kieker;
|
|
my $kieker = Kieker->new();
|
|
$_[0] =~ s/::/./g;
|
|
$_[0] =~ /^(.*)\..*?$/;
|
|
$kieker->ExitEvent($_[0],$1);
|
|
};
|
|
\end{lstlisting}
|
|
|
|
\noindent In diesem Fall werden in Zeile 2 alle EPrints Pakete für die Instrumentierung gewählt und jeweils vor (Zeilen 3 bis 9) und nach (Zeilen 10 bis 16) der Ausführung der Funktionen Entry- bzw. ExitEvents erzeugt. In den Events werden der Funktionsname und auch der Paketname als Parameter übergeben. Hierbei ist zu beachten dass in Perl der zweifache Doppelpunkt als Trennzeichen in Signaturen verwendet wird und nicht der in Java übliche einfache Punkt. Diese Notation wird hier mit regulären Ausdrücken (Zeilen 6, 7, 13 und 14) angepasst um später besser in \textsf{Kieker.Analysis} ausgewertet werden zu können.
|
|
|
|
Der angegebene Code kann an verschiedenen Stellen eingebracht werden. Eine Möglichkeit wäre es, direkt in der Konfiguration des Webservers diesen Code ausführen zu lassen. Somit könnten auch Initialisierungen der Webanwendungen mit in dem Monitoring erfasst werden. Leider hat sich in einer der letzten Perl-Updates die Behandlung von definierten Konstanten verändert, was zu einem Fehler in dem Modul Sub::WrapPackages führt. Dieser Fehler verhindert die Aktivierung des Moduls bevor durch die Anwendung die Verbindung zur Datenbank aufgebaut wurde, da sonst Typenfehler bei Datenbankanfragen auftreten.
|
|
|
|
\noindent Statt dessen wurde der Code in die CGI-Dateien eingebracht, die vom Benutzer aufgerufen werden. Somit konnte selektiv bestimmt werden, welche Requests von dem Monitoring erfasst werden sollten. Zusätzlich wurden in der CGI-Datei zu Beginn und Ende des Codes Entry- und ExitEvents eingefügt, um einen äußeren Rahmen für den Trace zu bieten. |