From b746ab36d6ee689eb0326c66cb9900bb20a0db36 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 25 Nov 2013 14:45:06 +0000 Subject: [PATCH] works! --- libvips/include/vips/gate.h | 1 + libvips/iofuncs/gate.c | 16 +++++- libvips/iofuncs/init.c | 6 ++- tools/vipsprofile.py | 103 ++++++++++++++++++++++++------------ 4 files changed, 89 insertions(+), 37 deletions(-) diff --git a/libvips/include/vips/gate.h b/libvips/include/vips/gate.h index 7d2e1928..afd1e62d 100644 --- a/libvips/include/vips/gate.h +++ b/libvips/include/vips/gate.h @@ -52,6 +52,7 @@ G_STMT_START { \ extern gboolean vips__thread_profile; void vips__thread_profile_attach( const char *thread_name ); +void vips__thread_profile_detach( void ); void vips__thread_profile_stop( void ); void vips__thread_gate_start( const char *gate_name ); diff --git a/libvips/iofuncs/gate.c b/libvips/iofuncs/gate.c index 95d88762..ca52da4d 100644 --- a/libvips/iofuncs/gate.c +++ b/libvips/iofuncs/gate.c @@ -114,7 +114,6 @@ vips_thread_profile_save( VipsThreadProfile *profile, FILE *fp ) g_mutex_unlock( vips__global_lock ); } - static void vips_thread_profile_free( VipsThreadProfile *profile ) { @@ -195,6 +194,21 @@ vips_thread_profile_get( void ) return( g_private_get( vips_thread_profile_key ) ); } +/* This usually happens automatically when a thread shuts down, see + * vips__thread_profile_init() where we set a GDestroyNotify, but will not + * happen for the main thread. + * + * Shut down any stats on the main thread with this, see vips_shutdown() + */ +void +vips__thread_profile_detach( void ) +{ + VipsThreadProfile *profile; + + if( (profile = vips_thread_profile_get()) ) + vips_thread_profile_free( profile ); +} + static VipsThreadGate * vips_thread_gate_new( const char *gate_name ) { diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index 3145891f..77c70b98 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -219,6 +219,9 @@ vips__init( const char *argv0 ) g_set_prgname( prgname ); g_free( prgname ); + vips__thread_profile_attach( "main" ); + VIPS_GATE_START( "main" ); + /* Try to discover our prefix. */ if( !(prefix = vips_guess_prefix( argv0, "VIPSHOME" )) || @@ -374,7 +377,8 @@ vips_shutdown( void ) im_close_plugins(); - vips__thread_profile_stop(); + VIPS_GATE_STOP( "main" ); + vips__thread_profile_detach(); /* In dev releases, always show leaks. But not more than once, it's * annoying. diff --git a/tools/vipsprofile.py b/tools/vipsprofile.py index a4d2b0d9..f2c86b93 100755 --- a/tools/vipsprofile.py +++ b/tools/vipsprofile.py @@ -10,21 +10,19 @@ class ReadFile: def __enter__(self): self.f = open(self.filename, 'r') - self.source = iter(self.f.readline, '') self.lineno = 0 self.getnext(); return self def __exit__(self, type, value, traceback): self.f.close() - return isinstance(value, StopIteration) def __nonzero__(self): return self.line != "" def getnext(self): self.lineno += 1 - self.line = self.source.next() + self.line = self.f.readline() def read_times(rf): times = [] @@ -56,18 +54,29 @@ class Event: self.work = False self.wait = False - if re.match('.*: .*work.*', gate_name): + if re.match('.*?: .*work.*', gate_name): self.work = True - if re.match('.*: .*wait.*', gate_name): + if re.match('.*?: .*wait.*', gate_name): self.wait = True thread.events.append(self) + +input_filename = 'vips-profile.txt' + thread_id = 0 threads = [] n_events = 0 -with ReadFile('vips-profile.txt') as rf: +print 'reading from', input_filename +with ReadFile(input_filename) as rf: while rf: + if rf.line.rstrip() == "": + rf.getnext() + continue + if rf.line[0] == "#": + rf.getnext() + continue + match = re.match('thread: (.*)', rf.line) if not match: print 'parse error line %d, expected "thread"' % rf.lineno @@ -82,6 +91,9 @@ with ReadFile('vips-profile.txt') as rf: if not match: break gate_name = match.group(1) + match = re.match('vips_(.*)', gate_name) + if match: + gate_name = match.group(1) rf.getnext() match = re.match('start:', rf.line) @@ -145,58 +157,79 @@ def is_overlap(events, gate_name1, gate_name2): # allocate a y position for each gate total_y = 0 for thread in threads: + thread.total_y = total_y + y = 1 gate_positions = {} for event in thread.events: if event.work or event.wait: gate_positions[event.gate_name] = 0 elif not event.gate_name in gate_positions: - overlap = True + no_overlap = False for gate_name in gate_positions: if not is_overlap(thread.events, gate_name, event.gate_name): gate_positions[event.gate_name] = gate_positions[gate_name] - overlap = False + no_overlap = True break - if overlap: + if not no_overlap: gate_positions[event.gate_name] = y y += 1 event.y = gate_positions[event.gate_name] - event.total_y = total_y + y + event.total_y = total_y + event.y total_y += y PIXELS_PER_SECOND = 1000 PIXELS_PER_GATE = 20 -WIDTH = int(last_time * PIXELS_PER_SECOND) + 100 -HEIGHT = int(total_y * PIXELS_PER_GATE) + 100 +LEFT_BORDER = 320 +BAR_HEIGHT = 5 +WIDTH = int(LEFT_BORDER + last_time * PIXELS_PER_SECOND) +HEIGHT = int((total_y + 1) * PIXELS_PER_GATE) -surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, WIDTH, HEIGHT) -ctx = cairo.Context (surface) +surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH + 50, HEIGHT) +ctx = cairo.Context(surface) +ctx.select_font_face('Sans') +ctx.set_font_size(15) -ctx.scale (PIXELS_PER_SECOND, PIXELS_PER_GATE) +def draw_event(ctx, event): + left = event.start * PIXELS_PER_SECOND + LEFT_BORDER + top = event.total_y * PIXELS_PER_GATE + width = (event.stop - event.start) * PIXELS_PER_SECOND - 1 + height = BAR_HEIGHT + + ctx.rectangle(left, top, width, height) + + if event.wait: + ctx.set_source_rgb(0.9, 0.1, 0.1) + elif event.work: + ctx.set_source_rgb(0.1, 0.9, 0.1) + else: + ctx.set_source_rgb(0.1, 0.1, 0.9) + + ctx.fill() + + if not event.wait and not event.work: + xbearing, ybearing, twidth, theight, xadvance, yadvance = \ + ctx.text_extents(event.gate_name) + + ctx.move_to(left + width / 2 - twidth / 2, top + theight) + ctx.set_source_rgb(1.00, 0.83, 0.00) + ctx.show_text(event.gate_name) + ctx.stroke() for thread in threads: + xbearing, ybearing, twidth, theight, xadvance, yadvance = \ + ctx.text_extents(thread.thread_name) + ctx.move_to(0, theight + thread.total_y * PIXELS_PER_GATE) + ctx.set_source_rgb(1.00, 1.00, 1.00) + ctx.show_text(thread.thread_name) + ctx.stroke() + for event in thread.events: - ctx.move_to (event.start, event.total_y) - ctx.line_to (event.stop, event.total_y) - ctx.close_path () - ctx.set_line_width (0.1) + draw_event(ctx, event) - if event.wait: - ctx.set_source_rgb (0.9, 0.1, 0.1) - elif event.work: - ctx.set_source_rgb (0.1, 0.9, 0.1) - else: - ctx.set_source_rgb (0.1, 0.1, 0.9) - - ctx.stroke () - - ctx.select_font_face('Georgia') - ctx.set_font_size(0.9) - ctx.move_to(event.start, event.total_y + 0.5) - ctx.set_source_rgb(1, 1, 1) - ctx.show_text(event.gate_name) - -surface.write_to_png ("example.png") # Output to PNG +output_filename = "example.png" +print 'writing to', output_filename +surface.write_to_png(output_filename)