d4e26e
From 5a0200975775f7bfbd51e1469e5560915800f791 Mon Sep 17 00:00:00 2001
d4e26e
From: Mark Reynolds <mreynolds@redhat.com>
d4e26e
Date: Tue, 23 Jun 2020 16:38:55 -0400
d4e26e
Subject: [PATCH] Issue 51165 - add new access log keywords for wtime and
d4e26e
 optime
d4e26e
d4e26e
Description:  In addition to the "etime" stat in the access we can also
d4e26e
              add the time the operation spent in the work queue, and
d4e26e
              how long the actual operation took.  We now have "wtime"
d4e26e
              and "optime" to track these stats in the access log.
d4e26e
d4e26e
              Also updated logconf for notes=F (related to a different
d4e26e
              ticket), and stats for wtime and optime.
d4e26e
d4e26e
relates: https://pagure.io/389-ds-base/issue/51165
d4e26e
d4e26e
Reviewed by: ?
d4e26e
---
d4e26e
 ldap/admin/src/logconv.pl         | 187 +++++++++++++++++++++++++++---
d4e26e
 ldap/servers/slapd/add.c          |   3 +
d4e26e
 ldap/servers/slapd/bind.c         |   4 +
d4e26e
 ldap/servers/slapd/delete.c       |   3 +
d4e26e
 ldap/servers/slapd/modify.c       |   3 +
d4e26e
 ldap/servers/slapd/modrdn.c       |   3 +
d4e26e
 ldap/servers/slapd/operation.c    |  24 ++++
d4e26e
 ldap/servers/slapd/opshared.c     |   3 +
d4e26e
 ldap/servers/slapd/result.c       |  50 ++++----
d4e26e
 ldap/servers/slapd/slap.h         |  13 ++-
d4e26e
 ldap/servers/slapd/slapi-plugin.h |  26 ++++-
d4e26e
 11 files changed, 270 insertions(+), 49 deletions(-)
d4e26e
d4e26e
diff --git a/ldap/admin/src/logconv.pl b/ldap/admin/src/logconv.pl
d4e26e
index e36386e11..250e9f9d4 100755
d4e26e
--- a/ldap/admin/src/logconv.pl
d4e26e
+++ b/ldap/admin/src/logconv.pl
d4e26e
@@ -3,7 +3,7 @@
d4e26e
 #
d4e26e
 # BEGIN COPYRIGHT BLOCK
d4e26e
 # Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
d4e26e
-# Copyright (C) 2013 Red Hat, Inc.
d4e26e
+# Copyright (C) 2020 Red Hat, Inc.
d4e26e
 # All rights reserved.
d4e26e
 #
d4e26e
 # License: GPL (version 3 or any later version).
d4e26e
@@ -55,7 +55,7 @@ my $reportStats = "";
d4e26e
 my $dataLocation = "/tmp";
d4e26e
 my $startTLSoid = "1.3.6.1.4.1.1466.20037";
d4e26e
 my @statnames=qw(last last_str results srch add mod modrdn moddn cmp del abandon
d4e26e
-                 conns sslconns bind anonbind unbind notesA notesU etime);
d4e26e
+                 conns sslconns bind anonbind unbind notesA notesU notesF etime);
d4e26e
 my $s_stats;
d4e26e
 my $m_stats;
d4e26e
 my $verb = "no";
d4e26e
@@ -211,6 +211,7 @@ my $sslClientBindCount = 0;
d4e26e
 my $sslClientFailedCount = 0;
d4e26e
 my $objectclassTopCount= 0;
d4e26e
 my $pagedSearchCount = 0;
d4e26e
+my $invalidFilterCount = 0;
d4e26e
 my $bindCount = 0;
d4e26e
 my $filterCount = 0;
d4e26e
 my $baseCount = 0;
d4e26e
@@ -258,7 +259,7 @@ map {$conn{$_} = $_} @conncodes;
d4e26e
 # hash db-backed hashes
d4e26e
 my @hashnames = qw(attr rc src rsrc excount conn_hash ip_hash conncount nentries
d4e26e
                    filter base ds6xbadpwd saslmech saslconnop bindlist etime oid
d4e26e
-                   start_time_of_connection end_time_of_connection
d4e26e
+                   start_time_of_connection end_time_of_connection notesf_conn_op
d4e26e
                    notesa_conn_op notesu_conn_op etime_conn_op nentries_conn_op
d4e26e
                    optype_conn_op time_conn_op srch_conn_op del_conn_op mod_conn_op
d4e26e
                    mdn_conn_op cmp_conn_op bind_conn_op unbind_conn_op ext_conn_op
d4e26e
@@ -926,7 +927,7 @@ if ($verb eq "yes" || $usage =~ /u/ || $usage =~ /U/){
d4e26e
 			}
d4e26e
 			while($op > 0){
d4e26e
 				# The bind op is not the same as the search op that triggered the notes=A.
d4e26e
-				# We have adjust the key by decrementing the op count until we find the last bind op.
d4e26e
+				# We have to adjust the key by decrementing the op count until we find the last bind op.
d4e26e
 				$op--;
d4e26e
 				$binddn_key = "$srvRstCnt,$conn,$op";
d4e26e
 				if (exists($bind_conn_op->{$binddn_key}) && defined($bind_conn_op->{$binddn_key})) {
d4e26e
@@ -1049,9 +1050,60 @@ if ($verb eq "yes" || $usage =~ /u/ || $usage =~ /U/){
d4e26e
 			}
d4e26e
 		}
d4e26e
 	}
d4e26e
-} # end of unindexed search report
d4e26e
+    print "\n";
d4e26e
+}
d4e26e
+
d4e26e
+print "Invalid Attribute Filters:    $invalidFilterCount\n";
d4e26e
+if ($invalidFilterCount > 0 && $verb eq "yes"){
d4e26e
+    my $conn_hash = $hashes->{conn_hash};
d4e26e
+    my $notesf_conn_op = $hashes->{notesf_conn_op};
d4e26e
+    my $time_conn_op = $hashes->{time_conn_op};
d4e26e
+    my $etime_conn_op = $hashes->{etime_conn_op};
d4e26e
+    my $nentries_conn_op = $hashes->{nentries_conn_op};
d4e26e
+    my $filter_conn_op = $hashes->{filter_conn_op};
d4e26e
+    my $bind_conn_op = $hashes->{bind_conn_op};
d4e26e
+    my $notesCount = 1;
d4e26e
+    my $unindexedIp;
d4e26e
+    my $binddn_key;
d4e26e
+    my %uniqFilt = (); # hash of unique filters
d4e26e
+    my %uniqFilter = (); # hash of unique filters bind dn
d4e26e
+    my %uniqBindDNs = (); # hash of unique bind dn's
d4e26e
+    my %uniqBindFilters = (); # hash of filters for a bind DN
d4e26e
+
d4e26e
+    while (my ($srcnt_conn_op, $count) = each %{$notesf_conn_op}) {
d4e26e
+        my ($srvRstCnt, $conn, $op) = split(",", $srcnt_conn_op);
d4e26e
+        my $attrIp = getIPfromConn($conn, $srvRstCnt);
d4e26e
+        print "\n  Invalid Attribute Filter #".$notesCount." (notes=F)\n";
d4e26e
+        print "  -  Date/Time:             $time_conn_op->{$srcnt_conn_op}\n";
d4e26e
+        print "  -  Connection Number:     $conn\n";
d4e26e
+        print "  -  Operation Number:      $op\n";
d4e26e
+        print "  -  Etime:                 $etime_conn_op->{$srcnt_conn_op}\n";
d4e26e
+        print "  -  Nentries:              $nentries_conn_op->{$srcnt_conn_op}\n";
d4e26e
+        print "  -  IP Address:            $attrIp\n";
d4e26e
+        if (exists($filter_conn_op->{$srcnt_conn_op}) && defined($filter_conn_op->{$srcnt_conn_op})) {
d4e26e
+            print "  -  Search Filter:         $filter_conn_op->{$srcnt_conn_op}\n";
d4e26e
+            $uniqFilt{$filter_conn_op->{$srcnt_conn_op}}++;
d4e26e
+        }
d4e26e
+        while($op > 0){
d4e26e
+            # The bind op is not the same as the search op that triggered the notes=A.
d4e26e
+            # We have to adjust the key by decrementing the op count until we find the last bind op.
d4e26e
+            $op--;
d4e26e
+            $binddn_key = "$srvRstCnt,$conn,$op";
d4e26e
+            if (exists($bind_conn_op->{$binddn_key}) && defined($bind_conn_op->{$binddn_key})) {
d4e26e
+                print "  -  Bind DN:               $bind_conn_op->{$binddn_key}\n";
d4e26e
+                $uniqBindDNs{$bind_conn_op->{$binddn_key}}++;
d4e26e
+                if( $uniqFilt{$filter_conn_op->{$srcnt_conn_op}} && defined($filter_conn_op->{$srcnt_conn_op})) {
d4e26e
+                    $uniqBindFilters{$bind_conn_op->{$binddn_key}}{$filter_conn_op->{$srcnt_conn_op}}++;
d4e26e
+                    $uniqFilter{$filter_conn_op->{$srcnt_conn_op}}{$bind_conn_op->{$binddn_key}}++;
d4e26e
+                }
d4e26e
+                last;
d4e26e
+            }
d4e26e
+        }
d4e26e
+        $notesCount++;
d4e26e
+    }
d4e26e
+    print "\n";
d4e26e
+}
d4e26e
 
d4e26e
-print "\n";
d4e26e
 print "FDs Taken:                    $fdTaken\n";
d4e26e
 print "FDs Returned:                 $fdReturned\n";
d4e26e
 print "Highest FD Taken:             $highestFdTaken\n\n";
d4e26e
@@ -1386,20 +1438,20 @@ if ($usage =~ /l/ || $verb eq "yes"){
d4e26e
 	}
d4e26e
 }
d4e26e
 
d4e26e
-#########################################
d4e26e
-#                                       #
d4e26e
-# Gather and Process the unique etimes  #
d4e26e
-#                                       #
d4e26e
-#########################################
d4e26e
+##############################################################
d4e26e
+#                                                            #
d4e26e
+# Gather and Process the unique etimes, wtimes, and optimes  #
d4e26e
+#                                                            #
d4e26e
+##############################################################
d4e26e
 
d4e26e
 my $first;
d4e26e
 if ($usage =~ /t/i || $verb eq "yes"){
d4e26e
+	# Print the elapsed times (etime)
d4e26e
+
d4e26e
 	my $etime = $hashes->{etime};
d4e26e
 	my @ekeys = keys %{$etime};
d4e26e
-	#
d4e26e
 	# print most often etimes
d4e26e
-	#
d4e26e
-	print "\n\n----- Top $sizeCount Most Frequent etimes -----\n\n";
d4e26e
+	print "\n\n----- Top $sizeCount Most Frequent etimes (elapsed times) -----\n\n";
d4e26e
 	my $eloop = 0;
d4e26e
 	my $retime = 0;
d4e26e
 	foreach my $et (sort { $etime->{$b} <=> $etime->{$a} } @ekeys) {
d4e26e
@@ -1411,16 +1463,84 @@ if ($usage =~ /t/i || $verb eq "yes"){
d4e26e
 		printf "%-8s        %-12s\n", $etime->{ $et }, "etime=$et";
d4e26e
 		$eloop++;
d4e26e
 	}
d4e26e
-	#
d4e26e
+	if ($eloop == 0) {
d4e26e
+		print "None";
d4e26e
+	}
d4e26e
 	# print longest etimes
d4e26e
-	#
d4e26e
-	print "\n\n----- Top $sizeCount Longest etimes -----\n\n";
d4e26e
+	print "\n\n----- Top $sizeCount Longest etimes (elapsed times) -----\n\n";
d4e26e
 	$eloop = 0;
d4e26e
 	foreach my $et (sort { $b <=> $a } @ekeys) {
d4e26e
 		if ($eloop == $sizeCount) { last; }
d4e26e
 		printf "%-12s    %-10s\n","etime=$et",$etime->{ $et };
d4e26e
 		$eloop++;
d4e26e
 	}
d4e26e
+	if ($eloop == 0) {
d4e26e
+		print "None";
d4e26e
+	}
d4e26e
+
d4e26e
+	# Print the wait times (wtime)
d4e26e
+
d4e26e
+	my $wtime = $hashes->{wtime};
d4e26e
+	my @wkeys = keys %{$wtime};
d4e26e
+	# print most often wtimes
d4e26e
+	print "\n\n----- Top $sizeCount Most Frequent wtimes (wait times) -----\n\n";
d4e26e
+	$eloop = 0;
d4e26e
+	$retime = 0;
d4e26e
+	foreach my $et (sort { $wtime->{$b} <=> $wtime->{$a} } @wkeys) {
d4e26e
+		if ($eloop == $sizeCount) { last; }
d4e26e
+		if ($retime ne "2"){
d4e26e
+			$first = $et;
d4e26e
+			$retime = "2";
d4e26e
+		}
d4e26e
+		printf "%-8s        %-12s\n", $wtime->{ $et }, "wtime=$et";
d4e26e
+		$eloop++;
d4e26e
+	}
d4e26e
+	if ($eloop == 0) {
d4e26e
+		print "None";
d4e26e
+	}
d4e26e
+	# print longest wtimes
d4e26e
+	print "\n\n----- Top $sizeCount Longest wtimes (wait times) -----\n\n";
d4e26e
+	$eloop = 0;
d4e26e
+	foreach my $et (sort { $b <=> $a } @wkeys) {
d4e26e
+		if ($eloop == $sizeCount) { last; }
d4e26e
+		printf "%-12s    %-10s\n","wtime=$et",$wtime->{ $et };
d4e26e
+		$eloop++;
d4e26e
+	}
d4e26e
+	if ($eloop == 0) {
d4e26e
+		print "None";
d4e26e
+	}
d4e26e
+
d4e26e
+	# Print the operation times (optime)
d4e26e
+
d4e26e
+	my $optime = $hashes->{optime};
d4e26e
+	my @opkeys = keys %{$optime};
d4e26e
+	# print most often optimes
d4e26e
+	print "\n\n----- Top $sizeCount Most Frequent optimes (actual operation times) -----\n\n";
d4e26e
+	$eloop = 0;
d4e26e
+	$retime = 0;
d4e26e
+	foreach my $et (sort { $optime->{$b} <=> $optime->{$a} } @opkeys) {
d4e26e
+		if ($eloop == $sizeCount) { last; }
d4e26e
+		if ($retime ne "2"){
d4e26e
+			$first = $et;
d4e26e
+			$retime = "2";
d4e26e
+		}
d4e26e
+		printf "%-8s        %-12s\n", $optime->{ $et }, "optime=$et";
d4e26e
+		$eloop++;
d4e26e
+	}
d4e26e
+	if ($eloop == 0) {
d4e26e
+		print "None";
d4e26e
+	}
d4e26e
+	# print longest optimes
d4e26e
+	print "\n\n----- Top $sizeCount Longest optimes (actual operation times) -----\n\n";
d4e26e
+	$eloop = 0;
d4e26e
+	foreach my $et (sort { $b <=> $a } @opkeys) {
d4e26e
+		if ($eloop == $sizeCount) { last; }
d4e26e
+		printf "%-12s    %-10s\n","optime=$et",$optime->{ $et };
d4e26e
+		$eloop++;
d4e26e
+	}
d4e26e
+	if ($eloop == 0) {
d4e26e
+		print "None";
d4e26e
+	}
d4e26e
 }
d4e26e
 
d4e26e
 #######################################
d4e26e
@@ -2152,6 +2272,26 @@ sub parseLineNormal
d4e26e
 	if (m/ RESULT err=/ && m/ notes=[A-Z,]*P/){
d4e26e
 		$pagedSearchCount++;
d4e26e
 	}
d4e26e
+	if (m/ RESULT err=/ && m/ notes=[A-Z,]*F/){
d4e26e
+		$invalidFilterCount++;
d4e26e
+		$con = "";
d4e26e
+		if ($_ =~ /conn= *([0-9A-Z]+)/i){
d4e26e
+			$con = $1;
d4e26e
+			if ($_ =~ /op= *([0-9\-]+)/i){ $op = $1;}
d4e26e
+		}
d4e26e
+
d4e26e
+		if($reportStats){ inc_stats('notesF',$s_stats,$m_stats); }
d4e26e
+        if ($usage =~ /u/ || $usage =~ /U/ || $verb eq "yes"){
d4e26e
+            if($_ =~ /etime= *([0-9.]+)/i ){
d4e26e
+                if($1 >= $minEtime){
d4e26e
+                    $hashes->{etime_conn_op}->{"$serverRestartCount,$con,$op"} = $1;
d4e26e
+                    $hashes->{notesf_conn_op}->{"$serverRestartCount,$con,$op"}++;
d4e26e
+                    if ($_ =~ / *([0-9a-z:\/]+)/i){ $hashes->{time_conn_op}->{"$serverRestartCount,$con,$op"} = $1; }
d4e26e
+                    if ($_ =~ /nentries= *([0-9]+)/i ){ $hashes->{nentries_conn_op}->{"$serverRestartCount,$con,$op"} = $1; }
d4e26e
+                }
d4e26e
+            }
d4e26e
+        }
d4e26e
+	}
d4e26e
 	if (m/ notes=[A-Z,]*A/){
d4e26e
 		$con = "";
d4e26e
 		if ($_ =~ /conn= *([0-9A-Z]+)/i){
d4e26e
@@ -2435,6 +2575,16 @@ sub parseLineNormal
d4e26e
 		if ($usage =~ /t/i || $verb eq "yes"){ $hashes->{etime}->{$etime_val}++; }
d4e26e
 		if ($reportStats){ inc_stats_val('etime',$etime_val,$s_stats,$m_stats); }
d4e26e
 	}
d4e26e
+	if ($_ =~ /wtime= *([0-9.]+)/ ) {
d4e26e
+		my $wtime_val = $1;
d4e26e
+		if ($usage =~ /t/i || $verb eq "yes"){ $hashes->{wtime}->{$wtime_val}++; }
d4e26e
+		if ($reportStats){ inc_stats_val('wtime',$wtime_val,$s_stats,$m_stats); }
d4e26e
+	}
d4e26e
+	if ($_ =~ /optime= *([0-9.]+)/ ) {
d4e26e
+		my $optime_val = $1;
d4e26e
+		if ($usage =~ /t/i || $verb eq "yes"){ $hashes->{optime}->{$optime_val}++; }
d4e26e
+		if ($reportStats){ inc_stats_val('optime',$optime_val,$s_stats,$m_stats); }
d4e26e
+	}
d4e26e
 	if ($_ =~ / tag=101 / || $_ =~ / tag=111 / || $_ =~ / tag=100 / || $_ =~ / tag=115 /){
d4e26e
 		if ($_ =~ / nentries= *([0-9]+)/i ){ 
d4e26e
 			my $nents = $1;
d4e26e
@@ -2555,7 +2705,7 @@ sub parseLineNormal
d4e26e
 			}
d4e26e
 		}
d4e26e
 	}
d4e26e
-	if (/ RESULT err=/ && / tag=97 nentries=0 etime=/ && $_ =~ /dn=\"(.*)\"/i){
d4e26e
+	if (/ RESULT err=/ && / tag=97 nentries=0 / && $_ =~ /dn=\"(.*)\"/i){
d4e26e
 		# Check if this is a sasl bind, if see we need to add the RESULT's dn as a bind dn
d4e26e
 		my $binddn = $1;
d4e26e
 		my ($conn, $op);
d4e26e
@@ -2678,6 +2828,7 @@ print_stats_block
d4e26e
 						 $stats->{'unbind'},
d4e26e
 						 $stats->{'notesA'},
d4e26e
 						 $stats->{'notesU'},
d4e26e
+						 $stats->{'notesF'},
d4e26e
 						 $stats->{'etime'}),
d4e26e
 					"\n" );
d4e26e
 			} else {
d4e26e
diff --git a/ldap/servers/slapd/add.c b/ldap/servers/slapd/add.c
d4e26e
index 8f2fdeac8..3a8d83899 100644
d4e26e
--- a/ldap/servers/slapd/add.c
d4e26e
+++ b/ldap/servers/slapd/add.c
d4e26e
@@ -440,6 +440,9 @@ op_shared_add(Slapi_PBlock *pb)
d4e26e
     internal_op = operation_is_flag_set(operation, OP_FLAG_INTERNAL);
d4e26e
     pwpolicy = new_passwdPolicy(pb, slapi_entry_get_dn(e));
d4e26e
 
d4e26e
+    /* Set the time we actually started the operation */
d4e26e
+    slapi_operation_set_time_started(operation);
d4e26e
+
d4e26e
     /* target spec is used to decide which plugins are applicable for the operation */
d4e26e
     operation_set_target_spec(operation, slapi_entry_get_sdn(e));
d4e26e
 
d4e26e
diff --git a/ldap/servers/slapd/bind.c b/ldap/servers/slapd/bind.c
d4e26e
index b3fe27327..0b1540cc3 100644
d4e26e
--- a/ldap/servers/slapd/bind.c
d4e26e
+++ b/ldap/servers/slapd/bind.c
d4e26e
@@ -87,6 +87,10 @@ do_bind(Slapi_PBlock *pb)
d4e26e
         send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);
d4e26e
         goto free_and_return;
d4e26e
     }
d4e26e
+
d4e26e
+    /* Set the time we actually started the operation */
d4e26e
+    slapi_operation_set_time_started(pb_op);
d4e26e
+
d4e26e
     ber = pb_op->o_ber;
d4e26e
 
d4e26e
     /*
d4e26e
diff --git a/ldap/servers/slapd/delete.c b/ldap/servers/slapd/delete.c
d4e26e
index 49cdab138..8846a4dea 100644
d4e26e
--- a/ldap/servers/slapd/delete.c
d4e26e
+++ b/ldap/servers/slapd/delete.c
d4e26e
@@ -234,6 +234,9 @@ op_shared_delete(Slapi_PBlock *pb)
d4e26e
     slapi_pblock_get(pb, SLAPI_OPERATION, &operation);
d4e26e
     internal_op = operation_is_flag_set(operation, OP_FLAG_INTERNAL);
d4e26e
 
d4e26e
+    /* Set the time we actually started the operation */
d4e26e
+    slapi_operation_set_time_started(operation);
d4e26e
+
d4e26e
     sdn = slapi_sdn_new_dn_byval(rawdn);
d4e26e
     dn = slapi_sdn_get_dn(sdn);
d4e26e
     slapi_pblock_set(pb, SLAPI_DELETE_TARGET_SDN, (void *)sdn);
d4e26e
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c
d4e26e
index f2f6d1783..a6894c305 100644
d4e26e
--- a/ldap/servers/slapd/modify.c
d4e26e
+++ b/ldap/servers/slapd/modify.c
d4e26e
@@ -634,6 +634,9 @@ op_shared_modify(Slapi_PBlock *pb, int pw_change, char *old_pw)
d4e26e
     slapi_pblock_get(pb, SLAPI_SKIP_MODIFIED_ATTRS, &skip_modified_attrs);
d4e26e
     slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);
d4e26e
 
d4e26e
+    /* Set the time we actually started the operation */
d4e26e
+    slapi_operation_set_time_started(operation);
d4e26e
+
d4e26e
     if (sdn) {
d4e26e
         passin_sdn = 1;
d4e26e
     } else {
d4e26e
diff --git a/ldap/servers/slapd/modrdn.c b/ldap/servers/slapd/modrdn.c
d4e26e
index 9483aafb5..a48ab8859 100644
d4e26e
--- a/ldap/servers/slapd/modrdn.c
d4e26e
+++ b/ldap/servers/slapd/modrdn.c
d4e26e
@@ -415,6 +415,9 @@ op_shared_rename(Slapi_PBlock *pb, int passin_args)
d4e26e
     internal_op = operation_is_flag_set(operation, OP_FLAG_INTERNAL);
d4e26e
     slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);
d4e26e
 
d4e26e
+    /* Set the time we actually started the operation */
d4e26e
+    slapi_operation_set_time_started(operation);
d4e26e
+
d4e26e
     /*
d4e26e
      * If ownership has not been passed to this function, we replace the
d4e26e
      * string input fields within the pblock with strdup'd copies.  Why?
d4e26e
diff --git a/ldap/servers/slapd/operation.c b/ldap/servers/slapd/operation.c
d4e26e
index 8186fd33b..bc5c692fe 100644
d4e26e
--- a/ldap/servers/slapd/operation.c
d4e26e
+++ b/ldap/servers/slapd/operation.c
d4e26e
@@ -660,3 +660,27 @@ slapi_operation_time_expiry(Slapi_Operation *o, time_t timeout, struct timespec
d4e26e
 {
d4e26e
     slapi_timespec_expire_rel(timeout, &(o->o_hr_time_rel), expiry);
d4e26e
 }
d4e26e
+
d4e26e
+/* Set the time the operation actually started */
d4e26e
+void
d4e26e
+slapi_operation_set_time_started(Slapi_Operation *o)
d4e26e
+{
d4e26e
+	clock_gettime(CLOCK_MONOTONIC, &(o->o_hr_time_started_rel));
d4e26e
+}
d4e26e
+
d4e26e
+/* The time diff of how long the operation took once it actually started */
d4e26e
+void
d4e26e
+slapi_operation_op_time_elapsed(Slapi_Operation *o, struct timespec *elapsed)
d4e26e
+{
d4e26e
+    struct timespec o_hr_time_now;
d4e26e
+    clock_gettime(CLOCK_MONOTONIC, &o_hr_time_now);
d4e26e
+
d4e26e
+    slapi_timespec_diff(&o_hr_time_now, &(o->o_hr_time_started_rel), elapsed);
d4e26e
+}
d4e26e
+
d4e26e
+/* The time diff the operation waited in the work queue */
d4e26e
+void
d4e26e
+slapi_operation_workq_time_elapsed(Slapi_Operation *o, struct timespec *elapsed)
d4e26e
+{
d4e26e
+    slapi_timespec_diff(&(o->o_hr_time_started_rel), &(o->o_hr_time_rel), elapsed);
d4e26e
+}
d4e26e
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
d4e26e
index b9fc83516..2afb8e2fc 100644
d4e26e
--- a/ldap/servers/slapd/opshared.c
d4e26e
+++ b/ldap/servers/slapd/opshared.c
d4e26e
@@ -276,6 +276,9 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
d4e26e
     slapi_pblock_get(pb, SLAPI_SEARCH_TARGET_SDN, &sdn;;
d4e26e
     slapi_pblock_get(pb, SLAPI_OPERATION, &operation);
d4e26e
 
d4e26e
+    /* Set the time we actually started the operation */
d4e26e
+    slapi_operation_set_time_started(operation);
d4e26e
+
d4e26e
     if (NULL == sdn) {
d4e26e
         sdn = slapi_sdn_new_dn_byval(base);
d4e26e
         slapi_pblock_set(pb, SLAPI_SEARCH_TARGET_SDN, sdn);
d4e26e
diff --git a/ldap/servers/slapd/result.c b/ldap/servers/slapd/result.c
d4e26e
index 61e7a70f9..807f58a2c 100644
d4e26e
--- a/ldap/servers/slapd/result.c
d4e26e
+++ b/ldap/servers/slapd/result.c
d4e26e
@@ -1912,21 +1912,31 @@ log_result(Slapi_PBlock *pb, Operation *op, int err, ber_tag_t tag, int nentries
d4e26e
     int internal_op;
d4e26e
     CSN *operationcsn = NULL;
d4e26e
     char csn_str[CSN_STRSIZE + 5];
d4e26e
-    char etime[ETIME_BUFSIZ];
d4e26e
+    char etime[ETIME_BUFSIZ] = {0};
d4e26e
+    char wtime[ETIME_BUFSIZ] = {0};
d4e26e
+    char optime[ETIME_BUFSIZ] = {0};
d4e26e
     int pr_idx = -1;
d4e26e
     int pr_cookie = -1;
d4e26e
     uint32_t operation_notes;
d4e26e
+    struct timespec o_hr_time_end;
d4e26e
 
d4e26e
     slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
d4e26e
     slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_COOKIE, &pr_cookie);
d4e26e
-
d4e26e
     internal_op = operation_is_flag_set(op, OP_FLAG_INTERNAL);
d4e26e
 
d4e26e
-    struct timespec o_hr_time_end;
d4e26e
+    /* total elapsed time */
d4e26e
     slapi_operation_time_elapsed(op, &o_hr_time_end);
d4e26e
+    snprintf(etime, ETIME_BUFSIZ, "%" PRId64 ".%.09" PRId64 "", (int64_t)o_hr_time_end.tv_sec, (int64_t)o_hr_time_end.tv_nsec);
d4e26e
+
d4e26e
+    /* wait time */
d4e26e
+    slapi_operation_workq_time_elapsed(op, &o_hr_time_end);
d4e26e
+    snprintf(wtime, ETIME_BUFSIZ, "%" PRId64 ".%.09" PRId64 "", (int64_t)o_hr_time_end.tv_sec, (int64_t)o_hr_time_end.tv_nsec);
d4e26e
+
d4e26e
+    /* op time */
d4e26e
+    slapi_operation_op_time_elapsed(op, &o_hr_time_end);
d4e26e
+    snprintf(optime, ETIME_BUFSIZ, "%" PRId64 ".%.09" PRId64 "", (int64_t)o_hr_time_end.tv_sec, (int64_t)o_hr_time_end.tv_nsec);
d4e26e
 
d4e26e
 
d4e26e
-    snprintf(etime, ETIME_BUFSIZ, "%" PRId64 ".%.09" PRId64 "", (int64_t)o_hr_time_end.tv_sec, (int64_t)o_hr_time_end.tv_nsec);
d4e26e
 
d4e26e
     slapi_pblock_get(pb, SLAPI_OPERATION_NOTES, &operation_notes);
d4e26e
 
d4e26e
@@ -1955,22 +1965,22 @@ log_result(Slapi_PBlock *pb, Operation *op, int err, ber_tag_t tag, int nentries
d4e26e
         if (!internal_op) {
d4e26e
             slapi_log_access(LDAP_DEBUG_STATS,
d4e26e
                              "conn=%" PRIu64 " op=%d RESULT err=%d"
d4e26e
-                             " tag=%" BERTAG_T " nentries=%d etime=%s%s%s"
d4e26e
+                             " tag=%" BERTAG_T " nentries=%d wtime=%s optime=%s etime=%s%s%s"
d4e26e
                              ", SASL bind in progress\n",
d4e26e
                              op->o_connid,
d4e26e
                              op->o_opid,
d4e26e
                              err, tag, nentries,
d4e26e
-                             etime,
d4e26e
+                             wtime, optime, etime,
d4e26e
                              notes_str, csn_str);
d4e26e
         } else {
d4e26e
             slapi_log_access(LDAP_DEBUG_ARGS,
d4e26e
                              "conn=%s op=%d RESULT err=%d"
d4e26e
-                             " tag=%" BERTAG_T " nentries=%d etime=%s%s%s"
d4e26e
+                             " tag=%" BERTAG_T " nentries=%d wtime=%s optime=%s etime=%s%s%s"
d4e26e
                              ", SASL bind in progress\n",
d4e26e
                              LOG_INTERNAL_OP_CON_ID,
d4e26e
                              LOG_INTERNAL_OP_OP_ID,
d4e26e
                              err, tag, nentries,
d4e26e
-                             etime,
d4e26e
+                             wtime, optime, etime,
d4e26e
                              notes_str, csn_str);
d4e26e
         }
d4e26e
     } else if (op->o_tag == LDAP_REQ_BIND && err == LDAP_SUCCESS) {
d4e26e
@@ -1984,22 +1994,22 @@ log_result(Slapi_PBlock *pb, Operation *op, int err, ber_tag_t tag, int nentries
d4e26e
         if (!internal_op) {
d4e26e
             slapi_log_access(LDAP_DEBUG_STATS,
d4e26e
                              "conn=%" PRIu64 " op=%d RESULT err=%d"
d4e26e
-                             " tag=%" BERTAG_T " nentries=%d etime=%s%s%s"
d4e26e
+                             " tag=%" BERTAG_T " nentries=%d wtime=%s optime=%s etime=%s%s%s"
d4e26e
                              " dn=\"%s\"\n",
d4e26e
                              op->o_connid,
d4e26e
                              op->o_opid,
d4e26e
                              err, tag, nentries,
d4e26e
-                             etime,
d4e26e
+                             wtime, optime, etime,
d4e26e
                              notes_str, csn_str, dn ? dn : "");
d4e26e
         } else {
d4e26e
             slapi_log_access(LDAP_DEBUG_ARGS,
d4e26e
                              "conn=%s op=%d RESULT err=%d"
d4e26e
-                             " tag=%" BERTAG_T " nentries=%d etime=%s%s%s"
d4e26e
+                             " tag=%" BERTAG_T " nentries=%d wtime=%s optime=%s etime=%s%s%s"
d4e26e
                              " dn=\"%s\"\n",
d4e26e
                              LOG_INTERNAL_OP_CON_ID,
d4e26e
                              LOG_INTERNAL_OP_OP_ID,
d4e26e
                              err, tag, nentries,
d4e26e
-                             etime,
d4e26e
+                             wtime, optime, etime,
d4e26e
                              notes_str, csn_str, dn ? dn : "");
d4e26e
         }
d4e26e
         slapi_ch_free((void **)&dn;;
d4e26e
@@ -2008,22 +2018,22 @@ log_result(Slapi_PBlock *pb, Operation *op, int err, ber_tag_t tag, int nentries
d4e26e
             if (!internal_op) {
d4e26e
                 slapi_log_access(LDAP_DEBUG_STATS,
d4e26e
                                  "conn=%" PRIu64 " op=%d RESULT err=%d"
d4e26e
-                                 " tag=%" BERTAG_T " nentries=%d etime=%s%s%s"
d4e26e
+                                 " tag=%" BERTAG_T " nentries=%d wtime=%s optime=%s etime=%s%s%s"
d4e26e
                                  " pr_idx=%d pr_cookie=%d\n",
d4e26e
                                  op->o_connid,
d4e26e
                                  op->o_opid,
d4e26e
                                  err, tag, nentries,
d4e26e
-                                 etime,
d4e26e
+                                 wtime, optime, etime,
d4e26e
                                  notes_str, csn_str, pr_idx, pr_cookie);
d4e26e
             } else {
d4e26e
                 slapi_log_access(LDAP_DEBUG_ARGS,
d4e26e
                                  "conn=%s op=%d RESULT err=%d"
d4e26e
-                                 " tag=%" BERTAG_T " nentries=%d etime=%s%s%s"
d4e26e
+                                 " tag=%" BERTAG_T " nentries=%d wtime=%s optime=%s etime=%s%s%s"
d4e26e
                                  " pr_idx=%d pr_cookie=%d \n",
d4e26e
                                  LOG_INTERNAL_OP_CON_ID,
d4e26e
                                  LOG_INTERNAL_OP_OP_ID,
d4e26e
                                  err, tag, nentries,
d4e26e
-                                 etime,
d4e26e
+                                 wtime, optime, etime,
d4e26e
                                  notes_str, csn_str, pr_idx, pr_cookie);
d4e26e
             }
d4e26e
         } else if (!internal_op) {
d4e26e
@@ -2037,11 +2047,11 @@ log_result(Slapi_PBlock *pb, Operation *op, int err, ber_tag_t tag, int nentries
d4e26e
             }
d4e26e
             slapi_log_access(LDAP_DEBUG_STATS,
d4e26e
                              "conn=%" PRIu64 " op=%d RESULT err=%d"
d4e26e
-                             " tag=%" BERTAG_T " nentries=%d etime=%s%s%s%s\n",
d4e26e
+                             " tag=%" BERTAG_T " nentries=%d wtime=%s optime=%s etime=%s%s%s%s\n",
d4e26e
                              op->o_connid,
d4e26e
                              op->o_opid,
d4e26e
                              err, tag, nentries,
d4e26e
-                             etime,
d4e26e
+                             wtime, optime, etime,
d4e26e
                              notes_str, csn_str, ext_str);
d4e26e
             if (pbtxt) {
d4e26e
                 /* if !pbtxt ==> ext_str == "".  Don't free ext_str. */
d4e26e
@@ -2051,11 +2061,11 @@ log_result(Slapi_PBlock *pb, Operation *op, int err, ber_tag_t tag, int nentries
d4e26e
             int optype;
d4e26e
             slapi_log_access(LDAP_DEBUG_ARGS,
d4e26e
                              "conn=%s op=%d RESULT err=%d"
d4e26e
-                             " tag=%" BERTAG_T " nentries=%d etime=%s%s%s\n",
d4e26e
+                             " tag=%" BERTAG_T " nentries=%d wtime=%s optime=%s etime=%s%s%s\n",
d4e26e
                              LOG_INTERNAL_OP_CON_ID,
d4e26e
                              LOG_INTERNAL_OP_OP_ID,
d4e26e
                              err, tag, nentries,
d4e26e
-                             etime,
d4e26e
+                             wtime, optime, etime,
d4e26e
                              notes_str, csn_str);
d4e26e
             /*
d4e26e
              *  If this is an unindexed search we should log it in the error log if
d4e26e
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
d4e26e
index 1d0a9cbeb..007c50b86 100644
d4e26e
--- a/ldap/servers/slapd/slap.h
d4e26e
+++ b/ldap/servers/slapd/slap.h
d4e26e
@@ -1522,16 +1522,17 @@ typedef struct slapi_operation_results
d4e26e
  */
d4e26e
 typedef struct op
d4e26e
 {
d4e26e
-    BerElement *o_ber;             /* ber of the request          */
d4e26e
-    ber_int_t o_msgid;             /* msgid of the request          */
d4e26e
-    ber_tag_t o_tag;               /* tag of the request          */
d4e26e
+    BerElement *o_ber;             /* ber of the request */
d4e26e
+    ber_int_t o_msgid;             /* msgid of the request */
d4e26e
+    ber_tag_t o_tag;               /* tag of the request */
d4e26e
     struct timespec o_hr_time_rel; /* internal system time op initiated */
d4e26e
     struct timespec o_hr_time_utc; /* utc system time op initiated */
d4e26e
-    int o_isroot;                  /* requestor is manager          */
d4e26e
+    struct timespec o_hr_time_started_rel; /* internal system time op started */
d4e26e
+    int o_isroot;                  /* requestor is manager */
d4e26e
     Slapi_DN o_sdn;                /* dn bound when op was initiated */
d4e26e
-    char *o_authtype;              /* auth method used to bind dn      */
d4e26e
+    char *o_authtype;              /* auth method used to bind dn */
d4e26e
     int o_ssf;                     /* ssf for this operation (highest between SASL and TLS/SSL) */
d4e26e
-    int o_opid;                    /* id of this operation          */
d4e26e
+    int o_opid;                    /* id of this operation */
d4e26e
     PRUint64 o_connid;             /* id of conn initiating this op; for logging only */
d4e26e
     void *o_handler_data;
d4e26e
     result_handler o_result_handler;
d4e26e
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
d4e26e
index a8b563dd1..9f7971ec1 100644
d4e26e
--- a/ldap/servers/slapd/slapi-plugin.h
d4e26e
+++ b/ldap/servers/slapd/slapi-plugin.h
d4e26e
@@ -8235,13 +8235,29 @@ void slapi_operation_time_elapsed(Slapi_Operation *o, struct timespec *elapsed);
d4e26e
  */
d4e26e
 void slapi_operation_time_initiated(Slapi_Operation *o, struct timespec *initiated);
d4e26e
 /**
d4e26e
- * Given an operation and a timeout, return a populate struct with the expiry
d4e26e
- * time of the operation suitable for checking with slapi_timespec_expire_check
d4e26e
+ * Given an operation, determine the time elapsed since the op
d4e26e
+ * was actually started.
d4e26e
  *
d4e26e
- * \param Slapi_Operation o - the operation that is in progress
d4e26e
- * \param time_t timeout the seconds relative to operation initiation to expiry at.
d4e26e
- * \param struct timespec *expiry the timespec to popluate with the relative expiry.
d4e26e
+ * \param Slapi_Operation o - the operation which is inprogress
d4e26e
+ * \param struct timespec *elapsed - location where the time difference will be
d4e26e
+ * placed.
d4e26e
+ */
d4e26e
+void slapi_operation_op_time_elapsed(Slapi_Operation *o, struct timespec *elapsed);
d4e26e
+/**
d4e26e
+ * Given an operation, determine the time elapsed that the op spent
d4e26e
+ * in the work queue before actually being dispatched to a worker thread
d4e26e
+ *
d4e26e
+ * \param Slapi_Operation o - the operation which is inprogress
d4e26e
+ * \param struct timespec *elapsed - location where the time difference will be
d4e26e
+ * placed.
d4e26e
+ */
d4e26e
+void slapi_operation_workq_time_elapsed(Slapi_Operation *o, struct timespec *elapsed);
d4e26e
+/**
d4e26e
+ * Set the time the operation actually started
d4e26e
+ *
d4e26e
+ * \param Slapi_Operation o - the operation which is inprogress
d4e26e
  */
d4e26e
+void slapi_operation_set_time_started(Slapi_Operation *o);
d4e26e
 #endif
d4e26e
 
d4e26e
 /**
d4e26e
-- 
d4e26e
2.30.2
d4e26e