diff --git a/aa_log_extract_tokens.pl b/aa_log_extract_tokens.pl
deleted file mode 100755
index bca2451b1be9a2b9f711106a71d5e5a78192aca5..0000000000000000000000000000000000000000
--- a/aa_log_extract_tokens.pl
+++ /dev/null
@@ -1,228 +0,0 @@
-#!/usr/bin/env perl
-# vim: set sts=4 sw=4 et tw=80 :
-#
-#    Copyright (c) 2006 Novell, Inc. All Rights Reserved.
-#    Copyright (c) 2010 Canonical, Ltd.
-#    Copyright (c) 2012-2015 Collabora Ltd.
-#
-#    This program is free software; you can redistribute it and/or
-#    modify it under the terms of version 2 of the GNU General Public
-#    License as published by the Free Software Foundation.
-#
-#    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, contact Novell, Inc.
-#
-#    To contact Novell about this file by physical or electronic mail,
-#    you may find current contact information at www.novell.com.
-
-# Example script to extract tokens from apparmor events in audit.log
-# We use auditd because otherwise messages are lost if they come too quickly
-# http://wiki.apparmor.net/index.php/AppArmorMonitoring#Realtime_information
-#
-#
-# Example complaint event:
-# type=AVC msg=audit(1344659645.579:153): apparmor="ALLOWED" operation="open"
-# parent=15215 profile="/home/chaiwala/bin/*" name="/etc/group" pid=15216
-# comm="ls" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
-#
-# Example audit event:
-# type=AVC msg=audit(1344735566.384:827): apparmor="AUDIT" operation="getattr"
-# parent=16769 profile="/home/chaiwala/bin/*" name="/etc/localtime" pid=16770
-# comm="ls" requested_mask="r" fsuid=1000 ouid=0
-#
-
-use strict;
-use warnings;
-
-use LibAppArmor qw();
-
-use constant DEBUG => $ENV{AA_LOG_DEBUG};
-
-my $event;
-my $record;
-my $value;
-my $profile_wanted;
-my @event_modes;
-my @skip_names;
-my %records_order;
-
-sub usage {
-    print("Usage: $0 <event mode> [event mode]...\n");
-    print("And pipe logs to it\n");
-    print("Valid values for [event mode] are: PERMITTING, REJECTING, AUDITING\n");
-}
-
-# Ensure there are some arguments
-if ($#ARGV < 0) {
-    usage();
-    exit(1);
-}
-
-# Ensure that the arguments are all valid
-foreach (@ARGV) {
-    SWITCH: {
-        if (/PERMITTING/) { last SWITCH; }
-        if (/REJECTING/) { last SWITCH; }
-        if (/AUDITING/) { last SWITCH; }
-        usage();
-        exit(1);
-    }
-}
-
-# Valid values we'll use: PERMITTING, REJECTING, AUDITING
-# Valid values that we're not interested in: STATUS, ERROR, UNKNOWN, HINT
-# PERMITTING is emitted when:
-# * AppArmor is in "normal" audit mode[1]
-# * A profile loaded in complain mode matches something
-# REJECTING is emitted when:
-# * AppArmor is in "normal" audit mode[1]
-# * A profile loaded in enforce mode matches something
-# AUDITING is emitted when:
-# * AppArmor is in "all" audit mode[1]
-#
-# 1. http://wiki.apparmor.net/index.php/AppArmor_Failures#Module_audit_settings
-@event_modes = @ARGV;
-
-# We're being lazy and getting arguments from the environment instead of doing 
-# getopt parsing
-$profile_wanted = $ENV{'AA_PROFILE_WANTED'};
-
-# Regex of file paths to skip accesses to
-@skip_names=(
-    "/etc/ld\.so\.cache",
-    "/lib/i386-linux-gnu/libc.*\.so",
-    "/dev/pts/.*",
-    "/dev/(null|zero|random|urandom)",
-);
-
-# Arbitrary order, but we want to keep it constant.
-# chaiwala-apparmor-tests rely on the following order.
-%records_order = (
-  'profile' => 1,
-  'sdmode' => 2,
-  'denied_mask' => 3,
-  'operation' => 4,
-  'name' => 5,
-  'request_mask' => 6,
-  );
-
-sub records_sort {
-  if (exists $records_order{$a} && exists $records_order{$b})
-  {
-    return ($records_order{$a}) <=> ($records_order{$b});
-  }
-
-  if (! exists $records_order{$a} && !exists $records_order{$b})
-  {
-    return $a cmp $b;
-  }
-
-  return exists $records_order{$a} ? -1 : 1;
-}
-
-# Simplified from Immunix::AppArmor.
-sub stringify_sdmode {
-    my $sdmode = shift;
-
-    return 'ERROR' if $sdmode == 1;
-    return 'AUDITING' if $sdmode == 2;
-    return 'PERMITTING' if $sdmode == 3;
-    return 'REJECTING' if $sdmode == 4;
-    return 'HINT' if $sdmode == 5;
-    return 'STATUS' if $sdmode == 6;
-
-    # no idea - output *something*
-    return $sdmode;
-}
-
-# Simplified from Immunix::AppArmor. We're not using its parse_event
-# because that only works properly for events whose mask is something
-# like r or w, not for events whose mask is more like receive or send;
-# this version is more "raw", and hopefully a little more robust against
-# AA changes.
-sub parse_event {
-    my %ev = ();
-    my $msg = shift;
-    chomp($msg);
-    my $event = LibAppArmor::parse_record($msg);
-
-    $ev{resource} = LibAppArmor::aa_log_record::swig_info_get($event);
-    $ev{active_hat} = LibAppArmor::aa_log_record::swig_active_hat_get($event);
-    $ev{sdmode} = LibAppArmor::aa_log_record::swig_event_get($event);
-    $ev{time} = LibAppArmor::aa_log_record::swig_epoch_get($event);
-    $ev{operation} = LibAppArmor::aa_log_record::swig_operation_get($event);
-    $ev{profile} = LibAppArmor::aa_log_record::swig_profile_get($event);
-    $ev{name} = LibAppArmor::aa_log_record::swig_name_get($event);
-    $ev{name2} = LibAppArmor::aa_log_record::swig_name2_get($event);
-    $ev{attr} = LibAppArmor::aa_log_record::swig_attribute_get($event);
-    $ev{parent} = LibAppArmor::aa_log_record::swig_parent_get($event);
-    $ev{pid} = LibAppArmor::aa_log_record::swig_pid_get($event);
-    $ev{task} = LibAppArmor::aa_log_record::swig_task_get($event);
-    $ev{info} = LibAppArmor::aa_log_record::swig_info_get($event);
-    $ev{denied_mask} = LibAppArmor::aa_log_record::swig_denied_mask_get($event);
-    $ev{request_mask} = LibAppArmor::aa_log_record::swig_requested_mask_get($event);
-    $ev{magic_token} = LibAppArmor::aa_log_record::swig_magic_token_get($event);
-    $ev{family} = LibAppArmor::aa_log_record::swig_net_family_get($event);
-    $ev{protocol} = LibAppArmor::aa_log_record::swig_net_protocol_get($event);
-    $ev{sock_type} = LibAppArmor::aa_log_record::swig_net_sock_type_get($event);
-
-    LibAppArmor::free_record($event);
-
-    if (! $ev{sdmode}) {
-        return undef;
-    }
-
-    $ev{sdmode} = stringify_sdmode($ev{sdmode});
-
-    if (DEBUG) {
-        print STDERR "/----\n";
-        foreach my $k (sort keys %ev) {
-            if (defined $ev{$k}) {
-                print STDERR "\t", $k, "\t", $ev{$k}, "\n";
-            }
-        }
-        print STDERR "\\----\n";
-    }
-
-    return \%ev;
-}
-
-EVENT: while (<STDIN>) {
-    # This doesn't give us all the tokens, unfortunately.
-    # Only gives us: profile, sdmode, time, denied_mask, pid, operation,
-    # parent (parent pid), name (filename), request_mask
-    # (FIXME: it could now give more if we want them)
-    $event = parse_event($_);
-
-    if (!$event ||
-        !(grep { $_ eq $$event{'sdmode'} } @event_modes) ||
-        ($profile_wanted && ($$event{'profile'} ne $profile_wanted)))
-    {
-        next EVENT;
-    }
-
-    foreach (@skip_names) {
-        if (defined $$event{'name'} && $$event{'name'} =~ /$_/) {
-            next EVENT;
-        }
-    }
-
-    print "====\n";
-    # This is pretty much it. Go forth and use this hash in whatever way.
-    foreach $record (sort records_sort keys %$event) {
-        $value = $$event{$record};
-        next unless $value;
-        # Skip records that keep changing with every invocation
-        SWITCH: {
-            if ($record eq "time") { last SWITCH; }
-            if ($record eq "pid") { last SWITCH; }
-            if ($record eq "parent") { last SWITCH; }
-            print "$record:$value\n";
-        }
-    }
-}
diff --git a/common-apparmor.sh b/common-apparmor.sh
new file mode 100644
index 0000000000000000000000000000000000000000..f6a94977038e82708ec2f870dde7df9f396828f6
--- /dev/null
+++ b/common-apparmor.sh
@@ -0,0 +1,43 @@
+# Need to source this file.
+
+# Pass in audit string, returns formatted block as `aa_log_extract_tokens.pl`
+# would have provided. Need to return:
+#
+# profile:/usr/bin/busctl
+# sdmode:REJECTING
+# denied_mask:r
+# operation:open
+# name:/proc/879/stat
+# request_mask:r
+#
+# Note: apparmor uses the term "DENIED" rather than "REJECTING", but our
+# expected values want "REJECTING", so tweak for now.
+apparmor_parse_journal() {
+	awk -v event=$2 '
+		{
+			for (i = 1; i <= NF; i++) {
+				n = index($i, "=");
+				if(n) {
+					vars[substr($i, 1, n - 1)] = substr($i, n + 1)
+					# Strip quotes
+					gsub("\"","",vars[substr($i, 1, n - 1)])
+				}
+			}
+		}
+		{
+			if (vars["apparmor"] == event) {
+				print "===="
+				print "profile:"vars["profile"]
+				# Match old nomenclature
+				if (vars["apparmor"] == "DENIED") {
+					vars["apparmor"] = "REJECTING"
+				}
+				print "sdmode:"vars["apparmor"]
+				print "denied_mask:"vars["denied_mask"]
+				print "operation:"vars["operation"]
+				print "name:"vars["name"]
+				print "request_mask:"vars["requested_mask"]
+			}
+		}
+	' $1
+}
diff --git a/run-aa-test b/run-aa-test
index 59399401325f12883307958c7d5c432f659221b3..ad6ec39bd77d976c4606991e96913e9b71141800 100755
--- a/run-aa-test
+++ b/run-aa-test
@@ -8,6 +8,8 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+. common/common-apparmor.sh
+
 ALTERNATIVE_SEPARATOR="## alternative ##"
 END=2
 
@@ -52,7 +54,6 @@ if [ ! -x $1 ]; then
 	exit 1
 fi
 
-SOMETHING_FAILED="False"
 # typically "normal.expected" or "malicious.expected"
 TEST_TITLE=$( basename ${EXPECT_FILE} )
 
@@ -112,7 +113,8 @@ else
 	echo "# ${CMDLINE} exited ${RET}"
 	# typically "normal.expected_underlying_tests: fail"
 	echo "${TEST_TITLE}_underlying_tests: fail"
-	SOMETHING_FAILED="True"
+
+	exit 1
 fi
 
 # Give journal time to log the entries.
@@ -124,11 +126,11 @@ journalctl -S "${START_TIME}" -t audit -o cat > ${AUDIT_FILE}
 
 echo "#=== ${TEST_TITLE} ==="
 
-echo "#---8<--- raw output in audit log"
+echo "#---8<--- raw apparmor output from journal"
 cat ${AUDIT_FILE} | sed 's/^/# /'
 echo "#--->8---"
 
-echo "#---8<--- expected output from aa_log_extract_tokens.pl"
+echo "#---8<--- expected parsed apparmor output from journal"
 cat ${EXPECT_FILE} | sed 's/^/# /'
 echo "#--->8---"
 
@@ -141,29 +143,12 @@ done
 
 PARSE_FILE="${TMP_DIR}/PARSE"
 
-RET=$( cat ${AUDIT_FILE} | common/aa_log_extract_tokens.pl REJECTING > ${PARSE_FILE} )
-
-if [ "${RET}" != "0" ]; then
-	echo "# aa_log_extract_tokens.pl failed, trying line-by-line..."
-
-	LINES=$(wc -l ${AUDIT_FILE} | cut -d ' ' -f1 )
-	
-	cat ${AUDIT_FILE} | while read LINE; do
-		echo ${LINE} | common/aa_log_extract_tokens.pl REJECTING 2>${TMP_DIR}/STDERR > ${TMP_DIR}/STDOUT
-		RET=$?
-		cat ${TMP_DIR}/STDOUT >> ${TMP_DIR}/ERRPARSE
-
-		cat ${TMP_DIR}/STDERR | sed 's/^/E: /' >> ${TMP_DIR}/ERRPARSE
+# TODO: There is potential for other processes to cause messages to appear in
+#       the journal that may lead to false failures. If this is found to be an
+#       issue in practice, then additional filtering of results may be required
+apparmor_parse_journal ${AUDIT_FILE} DENIED > ${PARSE_FILE}
 
-		if [ "$RET" != "0" ]; then
-			echo -n "^ original line: ${LINE}" >> ${TMP_DIR}/ERRPARSE
-		fi
-	done
-
-	mv ${TMP_DIR}/ERRPARSE ${PARSE_FILE}
-fi
-
-echo "#---8<--- actual output from aa_log_extract_tokens.pl"
+echo "#---8<--- actual parsed apparmor output from journal"
 cat ${PARSE_FILE} | sed 's/^/# /'
 echo "#--->8---"
 
@@ -189,10 +174,6 @@ else
 	diff -urN ${TMP_DIR}/EXPECT${NUM} ${PARSE_FILE}
 	echo "#--->8---"
 	echo "${TEST_TITLE}: fail"
-	SOMETHING_FAILED="True"
-fi
-
-if [ "${SOMETHING_FAILED}" = "True" ]; then
 	exit 1
 fi