From 7bb8b706c50570383c4acb20d44e5b59f6a4e6db Mon Sep 17 00:00:00 2001 From: Hallvard Furuseth Date: Wed, 11 Dec 2013 14:40:45 +0100 Subject: [PATCH] ITS#6758 Add SLAP_OP() &co, contrib:wrap_slap_ops. Add framework for debug macros SLAP_OP(), slap_be_op(), slap_bi_op() around LDAP-operation backend calls. contrib/slapd-tools/wrap_slap_ops converts code to use them. Code compiles as before by default. #define USE_RS_ASSERT enables debugging, $NO_RS_ASSERT tweaks it. See slapd/result.c. --- contrib/slapd-tools/README | 5 + contrib/slapd-tools/wrap_slap_ops | 162 ++++++++++++++++++++++++++++++ servers/slapd/backend.c | 63 ++++++++++++ servers/slapd/proto-slap.h | 10 ++ servers/slapd/result.c | 16 ++- 5 files changed, 255 insertions(+), 1 deletion(-) create mode 100755 contrib/slapd-tools/wrap_slap_ops diff --git a/contrib/slapd-tools/README b/contrib/slapd-tools/README index 2fe80276b2..4f5f12f924 100644 --- a/contrib/slapd-tools/README +++ b/contrib/slapd-tools/README @@ -5,6 +5,11 @@ statslog (LDAP request/response log), grouping log lines by LDAP connection. Useful to search and inspect the server log. +wrap_slap_ops + Update source code to use the wrapper macros SLAP_OP() & co + for LDAP-operation backend calls. They can help debug the + SlapReply. They compile like the old code by default. + --- Copyright 2004-2013 The OpenLDAP Foundation. All rights reserved. diff --git a/contrib/slapd-tools/wrap_slap_ops b/contrib/slapd-tools/wrap_slap_ops new file mode 100755 index 0000000000..09f6fb62f6 --- /dev/null +++ b/contrib/slapd-tools/wrap_slap_ops @@ -0,0 +1,162 @@ +#!/usr/bin/perl -wn0777 +# wrap_slap_ops - Help update code to use SLAP_OP() & co. +# +# This work is part of OpenLDAP Software . +# +# Copyright 2011-2013 The OpenLDAP Foundation. +# Portions Copyright 2011-2013 Hallvard B. Furuseth. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted only as authorized by the OpenLDAP +# Public License. +# +# A copy of this license is available in the file LICENSE in the +# top-level directory of the distribution or, alternatively, at +# . + +use strict; + +sub usage() { + warn "Usage: $0 {-l | -u | -U} {file | dir}... + +Update slapd source code to wrap LDAP operation calls in the debug +macros SLAP_OP() & co. They compile like the old code by default. +Define USE_RS_ASSERT to enable asserts which verify the SlapReply. +See servers/slapd/result.c. + +Options: + -u, -U Output unidiffs with n lines of context (-u = default for diff). + -l List files which would change. Show remaining cases on stderr.\n"; + exit(1); +} + +#### File/option handling. Skips symlinks, handles filenames =~ /\.[ch]+p*$/i. + +sub ls_R { + map { -l $_ ? () : -d _ ? ls_R(<$_/*>) : /\.[ch]+p*$/i ? $_ : () } @_; +} + +use constant Mode => shift(@ARGV) || ""; +use vars qw($ccnt $rcnt); +INIT { + usage() unless Mode =~ /^-(l|[uU]\d*)$/ && ($ARGV[0]||"") =~ /^[^\-]/; + exit(0) unless @ARGV = ls_R(@ARGV); # Expand ARGV, exit if no files + $| = 1; + $ccnt = $rcnt = 0; +} + +sub file_result( $$ ) { + my($contents, $changed) = @_; + $ccnt++ if $changed; + $rcnt += scalar( my @rest = remaining($contents) ); + if (Mode eq "-l") { + print "$ARGV\n" if $changed; + print STDERR "$ARGV:\t$_\n" foreach @rest; + } elsif ($changed) { + (my $file = "$ARGV") =~ s%^-%./-%; + print "Index: $file\n"; + (open(D, "|-", "diff", Mode, $file, "-") + && (print D $contents) + && (close(D) || $! == 0)) or die "$0: diff failed: $!\n"; + } +} + +END { + print STDERR <|\.) \s* \w+)*?)/x; + %addr = ("." => "&", "->" => ""); # x.y => (&x)->y, x->y => x->y + %func2op = map { /(\w+) \s+ (?= .*?=>\s* (\w+))/gx } ; + $func_re = '\b(?=b[ei]_)(?:' . join("|", keys %func2op) . ')\b'; + my %both = (%func2op, reverse %func2op); + $todo_re = '\b(?=[bo][eip]_)(?:' . join("|", keys %both) . ')\b'; +} +next if !/$todo_re/; +my $orig = "$_"; + +# x->func(op, rs) ==> slap_bi_op( x, , op, rs) +# x. func(op, rs) ==> slap_bi_op(&x, , op, rs) +s%( # 1: entire match: "(" + ((?: [\)!=\;{}\\] | \*/ | \b if\s*\( | \b return \b ) \s*) # 2: delim + (\(\s* (?:\* \s*)?)? # 3: optional "(*" or "(" in (*f)() + ($obj_re) \s* (->|\.) \s* # 4: object, 5: "->" or "." + (?=(b[ie]_))($func_re) \s* # 6: "bi_" or "be_", 7: function + (\)\s*)? # 8: optional ")" in (*f), + (\(\s*) # 9: "(" + whitespace +)% (!$3) == (!$8) ? "$2slap_$6op$9$addr{$5}$4, $func2op{$7}, " : $1 %egox; + +# (&x->bi_op_bind)[which](op, rs) ==> slap_bi_op(x, which, op, rs) +# (&x->be_bind)[which](op, rs) ==> slap_be_op(x, which, op, rs) +s/\(&(\w+)->b(?=([ei]))(?:e|i_op)_bind\)\[\s* (\w+) \s*\] \((\s*) ([^()]*)\) + /slap_b$2_op($4$1, $3, $5)/gox; + +# slap_bi_op(x->bd_info, which, op, rs) ==> slap_be_op( x, which, op, rs) +# slap_bi_op(x. bd_info, which, op, rs) ==> slap_be_op(&x, which, op, rs) +s/\b slap_bi_op (\(\s*) ($obj_re) \s* (->|\.) \s* bd_info \s*, + /slap_be_op$1$addr{$3}$2,/gox; + +# slap_be_op(op->o_bd, which, &op, rs) ==> SLAP_OP(which, op, rs) +# slap_be_op(op. o_bd, which, &op, rs) ==> SLAP_OP(which, &op, rs) +s/\b(slap_be_op (\(\s*) ($obj_re) \s*(->|\.)\s* o_bd, \s (\w+, \s (&?)\3,)) + / $addr{$4} eq $6 ? "SLAP_OP$2$5" : die "$ARGV: Bad syntax: $1\n" /egox; + +my $changed = $_ ne $orig; + +# When changing a file, do some whitespace cleanup too +if ($changed) { + s/\b ((SLAP_OP|slap_b[ei](func)?_op) \b .*?) [\ \t]+$ /$1/gmx; + s/\A\s*\n//; + s/\s*\z/\n/; +} + +file_result($_, $changed); + +#### + +# Return remaining lines that contain operation method names +sub remaining { + my($contents) = @_; + return $contents !~ /$func_re/o ? () : grep { + !/^\# [ \t]* define \s+ ($func_re|slap_bi_op\b) /x && + # Skip "if ( (&bi->bi_op_bind)[ which ] )" and variants + !/^(\} \s* else \s*)? if \s* \( \s* + \(& (\w+ | \(\s*\w+\s*=\s*$obj_re\s*\)) -> bi_op_bind\) + \s* \[ \s* \w+ \s* \] + \s* [&|\)]/ox; + } $contents =~ m% ^[\ \t]* (?=\S) ( + # The line contains a member opfunction + .*? (?:->|\.) \s* $func_re + + # Skip if the member function is assigned, compared, + # 'and/or'ed, followed by a word (this is a comment), or by + # ') {' or ') word' (function is the boolean in an if/while). + (?! \s* (?: [!=&|\w] | \)\s*[\{\w] )) + + .*? + ) \s*?$ %gmox; +} + +# %func2op: Member functions => slap_operation_t +__DATA__ +be_bind bi_op_bind => op_bind +be_unbind bi_op_unbind => op_unbind +be_search bi_op_search => op_search +be_compare bi_op_compare => op_compare +be_modify bi_op_modify => op_modify +be_modrdn bi_op_modrdn => op_modrdn +be_add bi_op_add => op_add +be_delete bi_op_delete => op_delete +be_abandon bi_op_abandon => op_abandon +be_extended bi_extended => op_extended +be_cancel bi_op_cancel => op_cancel +be_operational bi_operational => op_aux_operational +be_chk_referrals bi_chk_referrals => op_aux_chk_referrals +be_chk_controls bi_chk_controls => op_aux_chk_controls diff --git a/servers/slapd/backend.c b/servers/slapd/backend.c index 2893b92dfc..0e334d937d 100644 --- a/servers/slapd/backend.c +++ b/servers/slapd/backend.c @@ -882,6 +882,69 @@ send_result:; return rc; } +/* Inlined in proto-slap.h, sans assertions, when !(USE_RS_ASSERT) */ +int +(slap_bi_op)( + BackendInfo *bi, + slap_operation_t which, + Operation *op, + SlapReply *rs ) +{ + int rc; +#ifndef slap_bi_op + void (*rsCheck)( const SlapReply *rs ) = + which < op_aux_operational ? rs_assert_ready : rs_assert_ok; +#else +# define rsCheck(rs) ((void) 0) +#endif + BI_op_func *fn; + + assert( bi != NULL ); + assert( (unsigned) which < (unsigned) op_last ); + + fn = (&bi->bi_op_bind)[ which ]; + + assert( op != NULL ); + assert( rs != NULL ); + assert( fn != 0 ); + rsCheck( rs ); + + rc = fn( op, rs ); + +#ifndef slap_bi_op + if ( rc != SLAP_CB_CONTINUE && rc != SLAP_CB_BYPASS ) { + int err = rs->sr_err; + + if ( 0 ) /* TODO */ + if ( err == LDAP_COMPARE_TRUE || err == LDAP_COMPARE_FALSE ) { + assert( which == op_compare ); + assert( rc == LDAP_SUCCESS ); + } + + rsCheck = which < op_extended ? rs_assert_done : rs_assert_ok; + if ( which == op_aux_chk_referrals ) { + if ( rc == LDAP_SUCCESS ) rsCheck = rs_assert_ready; + else if ( rc == LDAP_REFERRAL ) rsCheck = rs_assert_done; + } else if ( which == op_bind ) { + if ( rc == LDAP_SUCCESS ) rsCheck = rs_assert_ok; + } + + /* TODO: Just what is the relation between rc and rs->sr_err? */ + if ( rc != err && + (rc != LDAP_SUCCESS || + (err != LDAP_COMPARE_TRUE && err != LDAP_COMPARE_FALSE)) ) + { + rs->sr_err = rc; + rsCheck( rs ); + rs->sr_err = err; + } + } + rsCheck( rs ); +#endif + + return rc; +} + int be_entry_release_rw( Operation *op, diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index 80372a9267..cd0dc3ff9e 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -376,6 +376,16 @@ LDAP_SLAPD_F (struct berval *) be_root_dn LDAP_P(( Backend *be )); LDAP_SLAPD_F (int) be_entry_get_rw LDAP_P(( Operation *o, struct berval *ndn, ObjectClass *oc, AttributeDescription *at, int rw, Entry **e )); + +/* "backend->ophandler(op,rs)" wrappers, applied by contrib:wrap_slap_ops */ +#define SLAP_OP(which, op, rs) slap_bi_op((op)->o_bd->bd_info, which, op, rs) +#define slap_be_op(be, which, op, rs) slap_bi_op((be)->bd_info, which, op, rs) +#if !(defined(USE_RS_ASSERT) && (USE_RS_ASSERT)) +#define slap_bi_op(bi, which, op, rs) ((&(bi)->bi_op_bind)[which](op, rs)) +#endif +LDAP_SLAPD_F (int) (slap_bi_op) LDAP_P(( BackendInfo *bi, + slap_operation_t which, Operation *op, SlapReply *rs )); + LDAP_SLAPD_F (int) be_entry_release_rw LDAP_P(( Operation *o, Entry *e, int rw )); #define be_entry_release_r( o, e ) be_entry_release_rw( o, e, 0 ) diff --git a/servers/slapd/result.c b/servers/slapd/result.c index 5858496b14..2d3348b270 100644 --- a/servers/slapd/result.c +++ b/servers/slapd/result.c @@ -132,7 +132,21 @@ slap_req2res( ber_tag_t tag ) return tag; } -/* SlapReply debugging, prodo-slap.h overrides it in OpenLDAP releases */ +/* + * SlapReply debugging enabled by USE_RS_ASSERT. + * + * Disabled by default, but compiled in (but still unused) when + * LDAP_TEST. #define USE_RS_ASSERT as nonzero to enable some + * assertions which check the SlapReply. USE_RS_ASSERT = 2 or higher + * check aggressively, currently some code fail these tests. + * + * Environment variable $NO_RS_ASSERT controls how USE_RS_ASSERT handles + * errors. > 0: ignore errors, 0: abort (the default), < 0: just warn. + * + * Wrap LDAP operation calls in macros SLAP_OP() & co from proto-slap.h + * to check the SlapReply. contrib/slapd-tools/wrap_slap_ops converts + * source code to use the macros. + */ #if defined(LDAP_TEST) || (defined(USE_RS_ASSERT) && (USE_RS_ASSERT)) int rs_suppress_assert = 0; -- 2.39.5