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