From 1560ef34c77a02f01ec2011d4c5b7c2e23aa3fb1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 18 Apr 2015 10:58:01 -0400 Subject: [PATCH] Wrote up the beginning of a system for testing object lifetimes. --- lifetimes.go | 149 ++++++++++++++++++++++++++++++++++++++++++++++ test.c | 2 +- unix/button.c | 2 +- unix/checkbox.c | 2 +- unix/entry.c | 2 +- unix/label.c | 2 +- unix/newcontrol.c | 6 +- unix/parent.c | 10 ++-- unix/tab.c | 2 +- 9 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 lifetimes.go diff --git a/lifetimes.go b/lifetimes.go new file mode 100644 index 00000000..644d05db --- /dev/null +++ b/lifetimes.go @@ -0,0 +1,149 @@ +// 18 april 2015 +package main + +import ( + "fmt" + "os" + "bufio" + "strings" + "strconv" +) + +func toaddr(s string) uintptr { + n, err := strconv.Itoa(s) + if err != nil { + panic(err) + } + return uintptr(n) +} + +var failed = false + +func report(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, format, args...) + failed = true +} + +type Object struct { + Type string + Detail string + Refs int + CanDestroy bool +} + +var objs = make(map[uintptr]*Object) +var singles = make(map[uintptr]*Object) + +var newControls = map[string]string{ + "uiNewButton()": "button", + "uiNewCheckbox()": "checkbox", + "uiNewEntry()": "entry", + "uiNewLabel()": "label", + "uiNewTab()": "tab", +} + +func newControl(parts []string) { + ty := newControls[parts[0]] + addr := toaddr(parts[1]) + detail := strings.Join(parts[2:], " ") + o := &Object{ + Type: ty, + Detail: detail, + Refs: 1, + } + if oo := objs[addr]; oo != nil { + report("duplicate object %s %q, %s %q at 0x%X\n", + oo.Type, oo.Detail, + o.Type, o.Detail, + addr) + } + if oo := singles[addr]; oo != nil { + report("single %s %q and objecct %s %q coexist at 0x%X\n", + oo.Type, oo.Detail, + o.Type, o.Detail, + addr) + } + objs[addr] = o +} + +func newSingle(parts []string) { + addrObj := toaddr(parts[1]) + addrSingle := toaddr(parts[2]) + o := objs[addrObj] + if o == nil { + report("single without object at 0x%X\n", addrObj) + } + o.Refs++ + if oo := singles[addrSingle]; oo != nil { + report("duplicate singles %s %q, %s %q at 0x%X\n", + oo.Type, oo.Detail, + o.Type, o.Detail, + addrSingle) + } + singles[addrSingle] = o +} + +func canDestroy(parts []string) { + addrObj := toaddr(parts[1]) + addrSingle := toaddr(parts[2]) + o := objs[addrObj] + if o == nil { + report("destroy object without object at 0x%X\n", addrObj) + } + if singles[addrSingle] != o { + report("inconsistency: single 0x%X has different object\n", addrSingle) + } + o.CanDestroy = true +} + +func destroy(parts []string) { + var o *Object + var what string + + ty := parts[1] + addr := toaddr(parts[2]) + if ty == "single" { + o = singles[addr] + delete(singles, addr) + what = "single" + } else { + o = objs[addr] + delete(objs, addr) + what = "object" + } + if o == nil { + report("missing %s %p in destroy()\n", what, addr) + return + } + if !o.CanDestroy { + report("can't destroy %s %p yet\n", what, addr) + } + o.Refs-- + if o.Refs < 0 { + report("not enough references to %p in destroy()\n", addr) + } +} + +func main() { + b := bufio.NewScanner(os.Stdin) + for b.Scan() { + s := b.String() + parts := strings.Split(s, " ") + name := parts[0] + switch { + case newControls[name] != "": + newControl(parts) + case name == "newSingle": + newSingle(parts) + case name == "uiControlDestroy()": + canDestroy(parts) + case name == "OSdestroy": + destroy(parts) + default: + panic("unknown line " + name) + } + } + if err := b.Err(); err != nil { + panic(err) + } +} diff --git a/test.c b/test.c index 38bd4c01..08d77ceb 100644 --- a/test.c +++ b/test.c @@ -196,7 +196,7 @@ int main(int argc, char *argv[]) for (i = 1; i < argc; i++) if (strcmp(argv[i], "leaks") == 0) o.debugLogAllocations = 1; - else if (strcmp(argv[i], "lifetime") == 0) + else if (strcmp(argv[i], "lifetimes") == 0) o.debugLogLifetimes = 1; else { fprintf(stderr, "%s: unrecognized option %s\n", argv[0], argv[i]); diff --git a/unix/button.c b/unix/button.c index ec2f8fdd..d4c0b92e 100644 --- a/unix/button.c +++ b/unix/button.c @@ -26,7 +26,7 @@ static void onDestroy(GtkWidget *widget, gpointer data) struct button *b = (struct button *) data; if (options.debugLogLifetimes) - fprintf(stderr, "GtkWidget::destroy button %p\n", b); + fprintf(stderr, "OSdestroy button %p\n", b); uiFree(b); } diff --git a/unix/checkbox.c b/unix/checkbox.c index 22ea702c..f7580e6f 100644 --- a/unix/checkbox.c +++ b/unix/checkbox.c @@ -29,7 +29,7 @@ static void onDestroy(GtkWidget *widget, gpointer data) struct checkbox *c = (struct checkbox *) data; if (options.debugLogLifetimes) - fprintf(stderr, "GtkWidget::destroy checkbox %p\n", c); + fprintf(stderr, "OSdestroy checkbox %p\n", c); uiFree(c); } diff --git a/unix/entry.c b/unix/entry.c index c2306984..665083a6 100644 --- a/unix/entry.c +++ b/unix/entry.c @@ -12,7 +12,7 @@ static void onDestroy(GtkWidget *widget, gpointer data) struct entry *e = (struct entry *) data; if (options.debugLogLifetimes) - fprintf(stderr, "GtkWidget::destroy entry %p\n", e); + fprintf(stderr, "OSdestroy entry %p\n", e); uiFree(e); } diff --git a/unix/label.c b/unix/label.c index 052fca2d..45ef07bb 100644 --- a/unix/label.c +++ b/unix/label.c @@ -12,7 +12,7 @@ static void onDestroy(GtkWidget *widget, gpointer data) struct label *l = (struct label *) data; if (options.debugLogLifetimes) - fprintf(stderr, "GtkWidget::destroy label %p\n", l); + fprintf(stderr, "OSdestroy label %p\n", l); uiFree(l); } diff --git a/unix/newcontrol.c b/unix/newcontrol.c index 56c685b1..a2e55743 100644 --- a/unix/newcontrol.c +++ b/unix/newcontrol.c @@ -19,7 +19,7 @@ static void singleDestroy(uiControl *c) singleWidget *s = (singleWidget *) (c->Internal); if (options.debugLogLifetimes) - fprintf(stderr, "uiControlDestroy() singleWidget %p %p\n", c, s); + fprintf(stderr, "uiControlDestroy() %p %p\n", c, s); gtk_widget_destroy(s->immediate); } @@ -164,7 +164,7 @@ static void onDestroy(GtkWidget *widget, gpointer data) singleWidget *s = (singleWidget *) data; if (options.debugLogLifetimes) - fprintf(stderr, "GtkWidget::destroy singleWidget %p\n", s); + fprintf(stderr, "OSdestroy single %p\n", s); uiFree(s); } @@ -175,7 +175,7 @@ void uiUnixNewControl(uiControl *c, GType type, gboolean inScrolledWindow, gbool s = uiNew(singleWidget); if (options.debugLogLifetimes) - fprintf(stderr, "uiUnixNewControl() %p %p\n", c, s); + fprintf(stderr, "newSingle %p %p\n", c, s); va_start(ap, firstProperty); s->widget = GTK_WIDGET(g_object_new_valist(type, firstProperty, ap)); diff --git a/unix/parent.c b/unix/parent.c index f7966f58..5d696f1c 100644 --- a/unix/parent.c +++ b/unix/parent.c @@ -32,7 +32,7 @@ static void uipParent_init(uipParent *p) if (options.debugLogAllocations) fprintf(stderr, "%p alloc uipParent\n", p); if (options.debugLogLifetimes) - fprintf(stderr, "uipParent_init() %p\n", p); +;//TODO fprintf(stderr, "uipParent_init() %p\n", p); p->children = g_ptr_array_new(); gtk_widget_set_has_window(GTK_WIDGET(p), FALSE); } @@ -44,7 +44,7 @@ static void uipParent_dispose(GObject *obj) uipParent *p = uipParent(obj); if (options.debugLogLifetimes) - fprintf(stderr, "uipParent_dispose() %p\n", p); +;//TODO fprintf(stderr, "uipParent_dispose() %p\n", p); if (p->children != NULL) { g_ptr_array_unref(p->children); p->children = NULL; @@ -59,7 +59,7 @@ static void uipParent_dispose(GObject *obj) static void uipParent_finalize(GObject *obj) { if (options.debugLogLifetimes) - fprintf(stderr, "uipParent_finalize() %p\n", obj); +;//TODO fprintf(stderr, "uipParent_finalize() %p\n", obj); G_OBJECT_CLASS(uipParent_parent_class)->finalize(obj); if (options.debugLogAllocations) fprintf(stderr, "%p free\n", obj); @@ -144,7 +144,7 @@ static void parentDestroy(uiParent *pp) uipParent *p = uipParent(pp->Internal); if (options.debugLogLifetimes) - fprintf(stderr, "uiParentDestroy() %p %p\n", pp, p); +;//TODO fprintf(stderr, "uiParentDestroy() %p %p\n", pp, p); gtk_widget_destroy(GTK_WIDGET(p)); } @@ -190,7 +190,7 @@ uiParent *uiNewParent(uintptr_t osParent) p = uiNew(uiParent); p->Internal = g_object_new(uipParentType, NULL); if (options.debugLogLifetimes) - fprintf(stderr, "uiNewParent() %p %p\n", p, p->Internal); +;//TODO fprintf(stderr, "uiNewParent() %p %p\n", p, p->Internal); p->Destroy = parentDestroy; p->Handle = parentHandle; p->SetMainControl = parentSetMainControl; diff --git a/unix/tab.c b/unix/tab.c index af38f665..a2666275 100644 --- a/unix/tab.c +++ b/unix/tab.c @@ -16,7 +16,7 @@ static void onDestroy(GtkWidget *widget, gpointer data) struct tab *t = (struct tab *) data; if (options.debugLogLifetimes) - fprintf(stderr, "GtkWidget::destroy tab %p\n", t); + fprintf(stderr, "OSdestroy tab %p\n", t); uiFree(t->pages); uiFree(t); }