1 /* profile.c: Z80 profiler
2    Copyright (c) 2005-2016 Philip Kendall
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation, Inc.,
16    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18    Author contact information:
19 
20    E-mail: Philip Kendall <philip-fuse@shadowmagic.org.uk>
21 
22 */
23 
24 #include <config.h>
25 
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include <libspectrum.h>
30 
31 #include "event.h"
32 #include "infrastructure/startup_manager.h"
33 #include "module.h"
34 #include "profile.h"
35 #include "ui/ui.h"
36 #include "z80/z80.h"
37 
38 int profile_active = 0;
39 
40 static int total_tstates[ 0x10000 ];
41 static libspectrum_word profile_last_pc;
42 static libspectrum_dword profile_last_tstates;
43 
44 static void profile_from_snapshot( libspectrum_snap *snap GCC_UNUSED );
45 
46 static module_info_t profile_module_info = {
47 
48   NULL,
49   NULL,
50   NULL,
51   profile_from_snapshot,
52   NULL,
53 
54 };
55 
56 static int
profile_init(void * context)57 profile_init( void *context )
58 {
59   module_register( &profile_module_info );
60 
61   return 0;
62 }
63 
64 void
profile_register_startup(void)65 profile_register_startup( void )
66 {
67   startup_manager_module dependencies[] = { STARTUP_MANAGER_MODULE_SETUID };
68   startup_manager_register( STARTUP_MANAGER_MODULE_PROFILE, dependencies,
69                             ARRAY_SIZE( dependencies ), profile_init, NULL,
70                             NULL );
71 }
72 
73 static void
init_profiling_counters(void)74 init_profiling_counters( void )
75 {
76   profile_last_pc = z80.pc.w;
77   profile_last_tstates = tstates;
78 }
79 
80 void
profile_start(void)81 profile_start( void )
82 {
83   memset( total_tstates, 0, sizeof( total_tstates ) );
84 
85   profile_active = 1;
86   init_profiling_counters();
87 
88   /* Schedule an event to ensure that the main z80 emulation loop recognises
89      profiling is turned on; otherwise problems occur if we started while
90      the debugger was active (bug #54) */
91   event_add( tstates, event_type_null );
92 
93   ui_menu_activate( UI_MENU_ITEM_MACHINE_PROFILER, 1 );
94 }
95 
96 void
profile_map(libspectrum_word pc)97 profile_map( libspectrum_word pc )
98 {
99   total_tstates[ profile_last_pc ] += tstates - profile_last_tstates;
100 
101   profile_last_pc = z80.pc.w;
102   profile_last_tstates = tstates;
103 }
104 
105 void
profile_frame(libspectrum_dword frame_length)106 profile_frame( libspectrum_dword frame_length )
107 {
108   profile_last_tstates -= frame_length;
109 }
110 
111 /* On snapshot load, PC and the tstate counter will jump so reset our
112    current views of these */
113 static void
profile_from_snapshot(libspectrum_snap * snap GCC_UNUSED)114 profile_from_snapshot( libspectrum_snap *snap GCC_UNUSED )
115 {
116   init_profiling_counters();
117 }
118 
119 void
profile_finish(const char * filename)120 profile_finish( const char *filename )
121 {
122   FILE *f;
123   size_t i;
124 
125   f = fopen( filename, "w" );
126   if( !f ) {
127     ui_error( UI_ERROR_ERROR, "unable to open profile map '%s' for writing",
128 	      filename );
129     return;
130   }
131 
132   for( i = 0; i < 0x10000; i++ ) {
133 
134     if( !total_tstates[ i ] ) continue;
135 
136     fprintf( f, "0x%04lx,%d\n", (unsigned long)i, total_tstates[ i ] );
137 
138   }
139 
140   fclose( f );
141 
142   profile_active = 0;
143 
144   /* Again, schedule an event to ensure this change is picked up by
145      the main loop */
146   event_add( tstates, event_type_null );
147 
148   ui_menu_activate( UI_MENU_ITEM_MACHINE_PROFILER, 0 );
149 }
150