From: Philipp Reisner <philipp.reisner@linbit.com>
To: Josh Triplett <josh@freedesktop.org>
Cc: Johannes Berg <johannes@sipsolutions.net>, linux-sparse@vger.kernel.org
Subject: Re: [PATCH 1/3] make sparse keep its promise about context tracking
Date: Thu, 10 Apr 2008 17:24:26 +0200 [thread overview]
Message-ID: <200804101724.27382.philipp.reisner@linbit.com> (raw)
In-Reply-To: <20080410132617.720109000@sipsolutions.net>
Am Donnerstag, 10. April 2008 15:25:20 schrieb Johannes Berg:
> The sparse man page promises that it will check this:
>
> Functions with the extended attribute
> __attribute__((context(expression,in_context,out_context))
> require the context expression (for instance, a lock) to have the
> value in_context (a constant nonnegative integer) when called,
> and return with the value out_context (a constant nonnegative
> integer).
>
> It doesn't keep that promise though, nor can it, especially with
> contexts that can be acquired recursively (like RCU in the kernel.)
>
Hi Josh,
Hi Johannes,
I just have implemented nearly the same here. Hopefully Josh will
decide for one of these patches soon.
diff --git a/expression.c b/expression.c
index 289927a..00cfb00 100644
--- a/expression.c
+++ b/expression.c
@@ -929,4 +929,76 @@ struct token *parse_expression(struct token *token,
struct expression **tree)
return comma_expression(token,tree);
}
+int ident_equal(struct ident *ident1, struct ident *ident2)
+{
+ return ident1->len == ident2->len &&
+ !strncmp(ident1->name, ident2->name, ident1->len);
+}
+
+int expressions_equal(struct expression *expr1, struct expression *expr2)
+{
+ if (expr1 == expr2)
+ return 1;
+
+ if (expr1 == NULL || expr2 == NULL)
+ return 0;
+
+ if (expr1->type != expr2->type)
+ return 0;
+
+ switch (expr1->type) {
+ case EXPR_SYMBOL:
+ return ident_equal(expr1->symbol_name, expr2->symbol_name);
+
+ case EXPR_VALUE:
+ return expr1->value == expr2->value;
+
+ case EXPR_FVALUE:
+ return expr1->fvalue == expr2->fvalue;
+
+ case EXPR_STRING:
+ return expr1->string->length == expr2->string->length &&
+ !strncmp(expr1->string->data, expr2->string->data, expr1->string->length);
+
+ case EXPR_BINOP:
+ return expr1->op == expr2->op &&
+ expressions_equal(expr1->left, expr2->left) &&
+ expressions_equal(expr1->right, expr2->right);
+
+ case EXPR_COMMA:
+ case EXPR_ASSIGNMENT:
+ return expressions_equal(expr1->left, expr2->left) &&
+ expressions_equal(expr1->right, expr2->right);
+
+ case EXPR_DEREF:
+ return expressions_equal(expr1->deref, expr2->deref) &&
+ ident_equal(expr1->member, expr2->member);
+
+ case EXPR_PREOP:
+ case EXPR_POSTOP:
+ return expr1->op == expr2->op &&
+ expressions_equal(expr1->unop, expr2->unop);
+
+ /* Not needed right now, but for sake of completness ...
+ case EXPR_LABEL:
+ case EXPR_STATEMENT:
+ case EXPR_CALL:
+ case EXPR_LOGICAL:
+ case EXPR_COMPARE:
+ case EXPR_SELECT:
+ case EXPR_CONDITIONAL:
+ case EXPR_CAST:
+ case EXPR_FORCE_CAST:
+ case EXPR_IMPLIED_CAST:
+ case EXPR_SLICE:
+ case EXPR_INITIALIZER:
+ case EXPR_POS:
+ */
+
+ default:
+ printf("Missing code in expressions_equal for %d\n", expr1->type);
+ }
+
+ return 0;
+}
diff --git a/expression.h b/expression.h
index 5136b9b..e89ddb7 100644
--- a/expression.h
+++ b/expression.h
@@ -216,4 +216,5 @@ struct token *compound_statement(struct token *, struct
statement *);
void cast_value(struct expression *expr, struct symbol *newtype,
struct expression *old, struct symbol *oldtype);
+extern int expressions_equal(struct expression *expr1, struct expression
*expr2);
#endif
diff --git a/linearize.c b/linearize.c
index 8a68f05..5aae3d6 100644
--- a/linearize.c
+++ b/linearize.c
@@ -67,6 +67,7 @@ static struct basic_block *alloc_basic_block(struct
entrypoint *ep, struct posit
{
struct basic_block *bb = __alloc_basic_block(0);
bb->context = -1;
+ bb->context_expr = NULL;
bb->pos = pos;
bb->ep = ep;
return bb;
diff --git a/linearize.h b/linearize.h
index 7b2961b..a71f9aa 100644
--- a/linearize.h
+++ b/linearize.h
@@ -225,6 +225,7 @@ struct basic_block {
struct position pos;
unsigned long generation;
int context;
+ struct expression *context_expr;
struct entrypoint *ep;
struct basic_block_list *parents; /* sources */
struct basic_block_list *children; /* destinations */
diff --git a/sparse.c b/sparse.c
index 4026ba7..50c5e51 100644
--- a/sparse.c
+++ b/sparse.c
@@ -24,7 +24,110 @@
#include "expression.h"
#include "linearize.h"
-static int context_increase(struct basic_block *bb, int entry)
+
+int ident_str(struct ident *ident, char *buffer, int length)
+{
+ return snprintf(buffer, length, "%.*s", ident->len, ident->name);
+}
+
+
+int expression_str(struct expression *expr, char *buffer, int length)
+{
+ int n;
+
+ if (!expr) {
+ buffer[0] = 0;
+ return 0;
+ }
+
+ /* TODO, think about necessary braces () */
+
+ switch (expr->type) {
+ case EXPR_SYMBOL:
+ return ident_str(expr->symbol_name, buffer, length);
+
+ case EXPR_VALUE:
+ return snprintf(buffer, length, "%llu", expr->value);
+
+ case EXPR_FVALUE:
+ return snprintf(buffer, length, "%Lf", expr->fvalue);
+
+ case EXPR_STRING:
+ return snprintf(buffer, length, "%.*s", expr->string->length,
expr->string->data);
+
+ case EXPR_BINOP:
+ n = expression_str(expr->left, buffer, length);
+ n += snprintf(buffer+n, length-n, "%c", expr->op);
+ n += expression_str(expr->right, buffer+n, length-n);
+ return n;
+
+ case EXPR_COMMA:
+ n = expression_str(expr->left, buffer, length);
+ n += snprintf(buffer+n, length-n, ",");
+ n += expression_str(expr->right, buffer+n, length-n);
+ return n;
+
+ case EXPR_ASSIGNMENT:
+ n = expression_str(expr->left, buffer, length);
+ n += snprintf(buffer+n, length-n, "=");
+ n += expression_str(expr->right, buffer+n, length-n);
+ return n;
+
+ case EXPR_DEREF:
+ if (expr->left->type == EXPR_PREOP &&
+ expr->left->op == '*') {
+ n = expression_str(expr->left->unop, buffer, length);
+ n += snprintf(buffer+n, length-n, "->");
+ n += ident_str(expr->member, buffer+n, length-n);
+ } else {
+ n = expression_str(expr->left, buffer, length);
+ n += snprintf(buffer+n, length-n, ".");
+ n += ident_str(expr->member, buffer+n, length-n);
+ }
+ return n;
+
+ case EXPR_PREOP:
+ n = snprintf(buffer, length, "%c", expr->op);
+ n += expression_str(expr->unop, buffer+n, length-n);
+ return n;
+
+ case EXPR_POSTOP:
+ n = expression_str(expr->unop, buffer, length);
+ n += snprintf(buffer+n, length-n, "%c", expr->op);
+ return n;
+
+ /* Not needed right now, but for sake of completness ...
+ case EXPR_LABEL:
+ case EXPR_STATEMENT:
+ case EXPR_CALL:
+ case EXPR_LOGICAL:
+ case EXPR_COMPARE:
+ case EXPR_SELECT:
+ case EXPR_CONDITIONAL:
+ case EXPR_CAST:
+ case EXPR_FORCE_CAST:
+ case EXPR_IMPLIED_CAST:
+ case EXPR_SLICE:
+ case EXPR_INITIALIZER:
+ case EXPR_POS:
+ */
+
+ default:
+ printf("Missing code in expression_str for %d\n", expr->type);
+ }
+
+ return 0;
+}
+
+char *expression_sstr(struct expression *expr)
+{
+ static char expr_string[37] = " for ";
+ static char empty[] = "";
+
+ return expression_str(expr, expr_string+5, 32) ? expr_string : empty;
+}
+
+static int context_increase(struct basic_block *bb, struct expression *expr,
int entry)
{
int sum = 0;
struct instruction *insn;
@@ -33,6 +136,8 @@ static int context_increase(struct basic_block *bb, int
entry)
int val;
if (insn->opcode != OP_CONTEXT)
continue;
+ if (!expressions_equal(expr, insn->context_expr))
+ continue;
val = insn->increment;
if (insn->check) {
int current = sum + entry;
@@ -49,18 +154,19 @@ static int context_increase(struct basic_block *bb, int
entry)
return sum;
}
-static int imbalance(struct entrypoint *ep, struct basic_block *bb, int
entry, int exit, const char *why)
+static int imbalance(struct basic_block *bb, struct expression *expr, int
entry, int exit, const char *why)
{
if (Wcontext) {
- struct symbol *sym = ep->name;
- warning(bb->pos, "context imbalance in '%s' - %s", show_ident(sym->ident),
why);
+ struct symbol *sym = bb->ep->name;
+ warning(bb->pos, "context imbalance in '%s' - %s%s",
show_ident(sym->ident), why,
+ expression_sstr(expr));
}
return -1;
}
-static int check_bb_context(struct entrypoint *ep, struct basic_block *bb,
int entry, int exit);
+static int check_bb_context(struct basic_block *bb, struct expression *expr,
int entry, int exit);
-static int check_children(struct entrypoint *ep, struct basic_block *bb, int
entry, int exit)
+static int check_children(struct basic_block *bb, struct expression *expr,
int entry, int exit)
{
struct instruction *insn;
struct basic_block *child;
@@ -69,32 +175,36 @@ static int check_children(struct entrypoint *ep, struct
basic_block *bb, int ent
if (!insn)
return 0;
if (insn->opcode == OP_RET)
- return entry != exit ? imbalance(ep, bb, entry, exit, "wrong count at
exit") : 0;
+ return entry != exit ? imbalance(bb, expr, entry, exit, "wrong count at
exit") : 0;
FOR_EACH_PTR(bb->children, child) {
- if (check_bb_context(ep, child, entry, exit))
+ if (check_bb_context(child, expr, entry, exit))
return -1;
} END_FOR_EACH_PTR(child);
return 0;
}
-static int check_bb_context(struct entrypoint *ep, struct basic_block *bb,
int entry, int exit)
+static int check_bb_context(struct basic_block *bb, struct expression *expr,
int entry, int exit)
{
+ int eq;
+
if (!bb)
return 0;
- if (bb->context == entry)
+ eq = expressions_equal(expr, bb->context_expr);
+ if (eq && bb->context == entry)
return 0;
/* Now that's not good.. */
- if (bb->context >= 0)
- return imbalance(ep, bb, entry, bb->context, "different lock contexts for
basic block");
+ if (eq && bb->context >= 0)
+ return imbalance(bb, expr, entry, bb->context, "different lock contexts for
basic block");
bb->context = entry;
- entry += context_increase(bb, entry);
+ bb->context_expr = expr;
+ entry += context_increase(bb, expr, entry);
if (entry < 0)
- return imbalance(ep, bb, entry, exit, "unexpected unlock");
+ return imbalance(bb, expr, entry, exit, "unexpected unlock");
- return check_children(ep, bb, entry, exit);
+ return check_children(bb, expr, entry, exit);
}
static void check_cast_instruction(struct instruction *insn)
@@ -195,7 +305,31 @@ static void check_call_instruction(struct instruction
*insn)
}
}
-static void check_one_instruction(struct instruction *insn)
+static void add_expression_once(struct expression_list **expr_list, struct
expression* expr)
+{
+ struct expression *e;
+
+ FOR_EACH_PTR(*expr_list, e) {
+ if (expressions_equal(e, expr))
+ return;
+ } END_FOR_EACH_PTR(e);
+
+ add_expression(expr_list, expr);
+}
+
+static void remove_expression(struct expression_list **expr_list, struct
expression* expr)
+{
+ struct expression *e;
+
+ FOR_EACH_PTR(*expr_list, e) {
+ if (expressions_equal(e, expr)) {
+ DELETE_CURRENT_PTR(e);
+ return;
+ }
+ } END_FOR_EACH_PTR(e);
+}
+
+static void check_one_instruction(struct instruction *insn, struct
expression_list **expr_list)
{
switch (insn->opcode) {
case OP_CAST: case OP_SCAST:
@@ -208,26 +342,29 @@ static void check_one_instruction(struct instruction
*insn)
case OP_CALL:
check_call_instruction(insn);
break;
+ case OP_CONTEXT:
+ add_expression_once(expr_list, insn->context_expr);
+ break;
default:
break;
}
}
-static void check_bb_instructions(struct basic_block *bb)
+static void check_bb_instructions(struct basic_block *bb, struct
expression_list **expr_list)
{
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
- check_one_instruction(insn);
+ check_one_instruction(insn, expr_list);
} END_FOR_EACH_PTR(insn);
}
-static void check_instructions(struct entrypoint *ep)
+static void check_instructions(struct entrypoint *ep, struct expression_list
**expr_list)
{
struct basic_block *bb;
FOR_EACH_PTR(ep->bbs, bb) {
- check_bb_instructions(bb);
+ check_bb_instructions(bb, expr_list);
} END_FOR_EACH_PTR(bb);
}
@@ -235,7 +372,8 @@ static void check_context(struct entrypoint *ep)
{
struct symbol *sym = ep->name;
struct context *context;
- unsigned int in_context = 0, out_context = 0;
+ struct expression_list *expr_list = NULL;
+ struct expression *expr;
if (Wuninitialized && verbose && ep->entry->bb->needs) {
pseudo_t pseudo;
@@ -246,13 +384,16 @@ static void check_context(struct entrypoint *ep)
} END_FOR_EACH_PTR(pseudo);
}
- check_instructions(ep);
+ check_instructions(ep, &expr_list);
FOR_EACH_PTR(sym->ctype.contexts, context) {
- in_context += context->in;
- out_context += context->out;
+ remove_expression(&expr_list, context->context);
+ check_bb_context(ep->entry->bb, context->context, context->in,
context->out);
} END_FOR_EACH_PTR(context);
- check_bb_context(ep, ep->entry->bb, in_context, out_context);
+
+ FOR_EACH_PTR(expr_list, expr) {
+ check_bb_context(ep->entry->bb, expr, 0, 0);
+ } END_FOR_EACH_PTR(expr);
}
static void check_symbols(struct symbol_list *list)
diff --git a/validation/named_context.c b/validation/named_context.c
new file mode 100644
index 0000000..9b6d77f
--- /dev/null
+++ b/validation/named_context.c
@@ -0,0 +1,32 @@
+#ifdef __CHECKER__
+# define __acquires(x) __attribute__((context(x,0,1)))
+# define __releases(x) __attribute__((context(x,1,0)))
+# define __acquire(x) __context__(x,1)
+# define __release(x) __context__(x,-1)
+#else
+# define __acquires(x)
+# define __releases(x)
+# define __acquire(x) (void)0
+# define __release(x) (void)0
+#endif
+
+static void invalid1(void)
+{
+ __acquire(lock1);
+ __release(lock2);
+}
+
+static void global_lock(void) __acquires(lock1) __acquires(lock2)
+{
+ __acquire(lock1);
+ __acquire(lock2);
+}
+
+/*
+ * check-name: Check -Wcontext
+ *
+ * check-error-start
+named_context.c:13:13: warning: context imbalance in 'invalid1' - wrong count
at exit for lock1
+named_context.c:13:13: warning: context imbalance in 'invalid1' - unexpected
unlock for lock2
+ * check-error-end
+ */
--
: Dipl-Ing Philipp Reisner Tel +43-1-8178292-50 :
: LINBIT Information Technologies GmbH Fax +43-1-8178292-82 :
: Vivenotgasse 48, 1120 Vienna, Austria http://www.linbit.com :
next prev parent reply other threads:[~2008-04-10 15:24 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-04-10 13:25 [PATCH 0/3] improve context handling Johannes Berg
2008-04-10 13:25 ` [PATCH 1/3] make sparse keep its promise about context tracking Johannes Berg
2008-04-10 15:24 ` Philipp Reisner [this message]
2008-04-10 15:30 ` Johannes Berg
2008-04-10 15:46 ` Philipp Reisner
2008-04-10 15:51 ` Johannes Berg
2008-04-10 16:05 ` Philipp Reisner
2008-04-10 16:12 ` Johannes Berg
2008-04-10 21:21 ` Philipp Reisner
2008-04-11 19:53 ` Josh Triplett
2008-04-18 12:35 ` Johannes Berg
2008-04-11 11:06 ` Johannes Berg
2008-04-21 19:34 ` Josh Triplett
2008-04-21 19:37 ` Johannes Berg
2008-04-10 15:54 ` Johannes Berg
2008-04-21 19:22 ` Josh Triplett
2008-04-21 18:04 ` Josh Triplett
2008-04-21 18:11 ` Johannes Berg
2008-04-21 18:26 ` Josh Triplett
2008-04-21 18:30 ` Johannes Berg
2008-04-21 18:51 ` Josh Triplett
2008-04-10 13:25 ` [PATCH 2/3] sparse test suite: add test mixing __context__ and __attribute__((context(...))) Johannes Berg
2008-04-10 13:25 ` [PATCH 3/3] sparse: simple conditional context tracking Johannes Berg
2008-04-11 11:07 ` [PATCH 4/3] inlined call bugfix & test Johannes Berg
2008-04-11 11:08 ` [PATCH 5/3] improve -Wcontext code and messages Johannes Berg
2008-04-21 18:37 ` [PATCH 0/3] improve context handling Josh Triplett
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200804101724.27382.philipp.reisner@linbit.com \
--to=philipp.reisner@linbit.com \
--cc=johannes@sipsolutions.net \
--cc=josh@freedesktop.org \
--cc=linux-sparse@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).