This commit is contained in:
John Cupitt 2013-11-25 14:45:06 +00:00
parent ba03c0921d
commit b746ab36d6
4 changed files with 89 additions and 37 deletions

View File

@ -52,6 +52,7 @@ G_STMT_START { \
extern gboolean vips__thread_profile; extern gboolean vips__thread_profile;
void vips__thread_profile_attach( const char *thread_name ); void vips__thread_profile_attach( const char *thread_name );
void vips__thread_profile_detach( void );
void vips__thread_profile_stop( void ); void vips__thread_profile_stop( void );
void vips__thread_gate_start( const char *gate_name ); void vips__thread_gate_start( const char *gate_name );

View File

@ -114,7 +114,6 @@ vips_thread_profile_save( VipsThreadProfile *profile, FILE *fp )
g_mutex_unlock( vips__global_lock ); g_mutex_unlock( vips__global_lock );
} }
static void static void
vips_thread_profile_free( VipsThreadProfile *profile ) vips_thread_profile_free( VipsThreadProfile *profile )
{ {
@ -195,6 +194,21 @@ vips_thread_profile_get( void )
return( g_private_get( vips_thread_profile_key ) ); 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 * static VipsThreadGate *
vips_thread_gate_new( const char *gate_name ) vips_thread_gate_new( const char *gate_name )
{ {

View File

@ -219,6 +219,9 @@ vips__init( const char *argv0 )
g_set_prgname( prgname ); g_set_prgname( prgname );
g_free( prgname ); g_free( prgname );
vips__thread_profile_attach( "main" );
VIPS_GATE_START( "main" );
/* Try to discover our prefix. /* Try to discover our prefix.
*/ */
if( !(prefix = vips_guess_prefix( argv0, "VIPSHOME" )) || if( !(prefix = vips_guess_prefix( argv0, "VIPSHOME" )) ||
@ -374,7 +377,8 @@ vips_shutdown( void )
im_close_plugins(); 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 /* In dev releases, always show leaks. But not more than once, it's
* annoying. * annoying.

View File

@ -10,21 +10,19 @@ class ReadFile:
def __enter__(self): def __enter__(self):
self.f = open(self.filename, 'r') self.f = open(self.filename, 'r')
self.source = iter(self.f.readline, '')
self.lineno = 0 self.lineno = 0
self.getnext(); self.getnext();
return self return self
def __exit__(self, type, value, traceback): def __exit__(self, type, value, traceback):
self.f.close() self.f.close()
return isinstance(value, StopIteration)
def __nonzero__(self): def __nonzero__(self):
return self.line != "" return self.line != ""
def getnext(self): def getnext(self):
self.lineno += 1 self.lineno += 1
self.line = self.source.next() self.line = self.f.readline()
def read_times(rf): def read_times(rf):
times = [] times = []
@ -56,18 +54,29 @@ class Event:
self.work = False self.work = False
self.wait = False self.wait = False
if re.match('.*: .*work.*', gate_name): if re.match('.*?: .*work.*', gate_name):
self.work = True self.work = True
if re.match('.*: .*wait.*', gate_name): if re.match('.*?: .*wait.*', gate_name):
self.wait = True self.wait = True
thread.events.append(self) thread.events.append(self)
input_filename = 'vips-profile.txt'
thread_id = 0 thread_id = 0
threads = [] threads = []
n_events = 0 n_events = 0
with ReadFile('vips-profile.txt') as rf: print 'reading from', input_filename
with ReadFile(input_filename) as rf:
while rf: while rf:
if rf.line.rstrip() == "":
rf.getnext()
continue
if rf.line[0] == "#":
rf.getnext()
continue
match = re.match('thread: (.*)', rf.line) match = re.match('thread: (.*)', rf.line)
if not match: if not match:
print 'parse error line %d, expected "thread"' % rf.lineno print 'parse error line %d, expected "thread"' % rf.lineno
@ -82,6 +91,9 @@ with ReadFile('vips-profile.txt') as rf:
if not match: if not match:
break break
gate_name = match.group(1) gate_name = match.group(1)
match = re.match('vips_(.*)', gate_name)
if match:
gate_name = match.group(1)
rf.getnext() rf.getnext()
match = re.match('start:', rf.line) 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 # allocate a y position for each gate
total_y = 0 total_y = 0
for thread in threads: for thread in threads:
thread.total_y = total_y
y = 1 y = 1
gate_positions = {} gate_positions = {}
for event in thread.events: for event in thread.events:
if event.work or event.wait: if event.work or event.wait:
gate_positions[event.gate_name] = 0 gate_positions[event.gate_name] = 0
elif not event.gate_name in gate_positions: elif not event.gate_name in gate_positions:
overlap = True no_overlap = False
for gate_name in gate_positions: for gate_name in gate_positions:
if not is_overlap(thread.events, gate_name, event.gate_name): if not is_overlap(thread.events, gate_name, event.gate_name):
gate_positions[event.gate_name] = gate_positions[gate_name] gate_positions[event.gate_name] = gate_positions[gate_name]
overlap = False no_overlap = True
break break
if overlap: if not no_overlap:
gate_positions[event.gate_name] = y gate_positions[event.gate_name] = y
y += 1 y += 1
event.y = gate_positions[event.gate_name] event.y = gate_positions[event.gate_name]
event.total_y = total_y + y event.total_y = total_y + event.y
total_y += y total_y += y
PIXELS_PER_SECOND = 1000 PIXELS_PER_SECOND = 1000
PIXELS_PER_GATE = 20 PIXELS_PER_GATE = 20
WIDTH = int(last_time * PIXELS_PER_SECOND) + 100 LEFT_BORDER = 320
HEIGHT = int(total_y * PIXELS_PER_GATE) + 100 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) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH + 50, HEIGHT)
ctx = cairo.Context (surface) 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: 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: for event in thread.events:
ctx.move_to (event.start, event.total_y) draw_event(ctx, event)
ctx.line_to (event.stop, event.total_y)
ctx.close_path ()
ctx.set_line_width (0.1)
if event.wait: output_filename = "example.png"
ctx.set_source_rgb (0.9, 0.1, 0.1) print 'writing to', output_filename
elif event.work: surface.write_to_png(output_filename)
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