| 1 | //#!C |
|---|
| 2 | /* |
|---|
| 3 | % |
|---|
| 4 | % Core of the NXC-Profiler! |
|---|
| 5 | % Declares various functions and macros to be included to NXC programs in order |
|---|
| 6 | % to get "live" runtime performance analysis. |
|---|
| 7 | % |
|---|
| 8 | % see http://forums.nxtasy.org/index.php?showtopic=4496 |
|---|
| 9 | % |
|---|
| 10 | % |
|---|
| 11 | % Signature |
|---|
| 12 | % Author: Linus Atorf (see AUTHORS) |
|---|
| 13 | % Date: 2009/12/07 |
|---|
| 14 | % Version: 0.1 |
|---|
| 15 | % License: GPLv3 |
|---|
| 16 | % |
|---|
| 17 | % |
|---|
| 18 | % *********************************************************************************************** |
|---|
| 19 | % * This file is part of the RWTH - Mindstorms NXT Toolbox. * |
|---|
| 20 | % * * |
|---|
| 21 | % * The RWTH - Mindstorms NXT Toolbox is free software: you can redistribute it and/or modify * |
|---|
| 22 | % * it under the terms of the GNU General Public License as published by the Free Software * |
|---|
| 23 | % * Foundation, either version 3 of the License, or (at your option) any later version. * |
|---|
| 24 | % * * |
|---|
| 25 | % * The RWTH - Mindstorms NXT Toolbox is distributed in the hope that it will be useful, * |
|---|
| 26 | % * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * |
|---|
| 27 | % * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * |
|---|
| 28 | % * * |
|---|
| 29 | % * You should have received a copy of the GNU General Public License along with the * |
|---|
| 30 | % * RWTH - Mindstorms NXT Toolbox. If not, see <http://www.gnu.org/licenses/>. * |
|---|
| 31 | % *********************************************************************************************** |
|---|
| 32 | */ |
|---|
| 33 | |
|---|
| 34 | //useful macros: |
|---|
| 35 | //#if __FIRMWARE_VERSION > 107 |
|---|
| 36 | //#ifdef __ENHANCED_FIRMWARE |
|---|
| 37 | //__LINE__ |
|---|
| 38 | |
|---|
| 39 | |
|---|
| 40 | // include-guard |
|---|
| 41 | #ifndef __PROFILER01_NXC__ |
|---|
| 42 | #define __PROFILER01_NXC__ |
|---|
| 43 | |
|---|
| 44 | |
|---|
| 45 | |
|---|
| 46 | //TEST: just a personal check to see if this compiles |
|---|
| 47 | //#define __LOCAL_COMPILE_FAKE_TEST |
|---|
| 48 | //#define PROFILER_ENABLE |
|---|
| 49 | //#define PROFILER_MAXSECTIONS 20 |
|---|
| 50 | //#define PROFILER_RESULTSFILE "test.prf" |
|---|
| 51 | |
|---|
| 52 | |
|---|
| 53 | //check if we do actually want to profile? |
|---|
| 54 | #ifndef PROFILER_ENABLE |
|---|
| 55 | |
|---|
| 56 | // these defines ensure that all "calls" to the profiler-functions |
|---|
| 57 | // are ignored and a commentsign (//) is included, so that |
|---|
| 58 | // PROFILER_BEGINSECTION(1); becomes //(1); which is harmless |
|---|
| 59 | #define PROFILER_START /##/ |
|---|
| 60 | #define PROFILER_BEGINSECTION /##/ |
|---|
| 61 | #define PROFILER_ENDSECTION /##/ |
|---|
| 62 | #define PROFILER_STOP /##/ |
|---|
| 63 | |
|---|
| 64 | #else // --- profiling enabled! ------------------------------- |
|---|
| 65 | |
|---|
| 66 | // check if user has set all options properly |
|---|
| 67 | #ifndef PROFILER_MAXSECTIONS |
|---|
| 68 | #error "PROFILER_MAXSECTIONS is undefined. Use #define PROFILER_MAXSECTIONS <number> to set the number of profile sections you're using!" |
|---|
| 69 | #endif |
|---|
| 70 | |
|---|
| 71 | #ifndef PROFILER_RESULTSFILE |
|---|
| 72 | #error "PROFILER_RESULTSFILE is undefined. Use #define PROFILER_RESULTSFILE ``sample.prf`` (with normal double-quotes) if your program is called ``sample.nxc``" |
|---|
| 73 | #endif |
|---|
| 74 | |
|---|
| 75 | |
|---|
| 76 | // global variables, these determine the memory usage! |
|---|
| 77 | long __PROFILER_GlobalStartTick; |
|---|
| 78 | long __PROFILER_ExecutionCount[PROFILER_MAXSECTIONS]; |
|---|
| 79 | long __PROFILER_ElapsedTicks[PROFILER_MAXSECTIONS]; |
|---|
| 80 | long __PROFILER_LastTick[PROFILER_MAXSECTIONS]; |
|---|
| 81 | long __PROFILER_ExecutionsSameMs[PROFILER_MAXSECTIONS]; |
|---|
| 82 | |
|---|
| 83 | |
|---|
| 84 | // init the whole profiling process! |
|---|
| 85 | inline void PROFILER_START() { |
|---|
| 86 | // not much to do, remember when we start. |
|---|
| 87 | DeleteFile(PROFILER_RESULTSFILE); |
|---|
| 88 | __PROFILER_GlobalStartTick = CurrentTick(); |
|---|
| 89 | }//end void |
|---|
| 90 | |
|---|
| 91 | |
|---|
| 92 | // at the begin of every code section to be profiled, we have: |
|---|
| 93 | inline void PROFILER_BEGINSECTION(const int &no) { |
|---|
| 94 | // just remember timestamp |
|---|
| 95 | __PROFILER_LastTick[no] = CurrentTick(); |
|---|
| 96 | }//end void |
|---|
| 97 | |
|---|
| 98 | |
|---|
| 99 | // at the end of every code section to be profiled, collect results: |
|---|
| 100 | inline void PROFILER_ENDSECTION(const int &no) { |
|---|
| 101 | // expensive function :-( |
|---|
| 102 | |
|---|
| 103 | // count the times this section has been executed |
|---|
| 104 | __PROFILER_ExecutionCount[no] = __PROFILER_ExecutionCount[no] + 1; |
|---|
| 105 | |
|---|
| 106 | // record elapsed time (if possible) |
|---|
| 107 | if (CurrentTick() != __PROFILER_LastTick[no]) { |
|---|
| 108 | // count the number of times where no tick has happened |
|---|
| 109 | __PROFILER_ExecutionsSameMs[no] = __PROFILER_ExecutionsSameMs[no] + 1; |
|---|
| 110 | } else { |
|---|
| 111 | // add up the ticked milliseconds -- not the best way? |
|---|
| 112 | __PROFILER_ElapsedTicks[no] = __PROFILER_ElapsedTicks[no] + (CurrentTick() - __PROFILER_LastTick[no]); |
|---|
| 113 | }//end if |
|---|
| 114 | }//end void |
|---|
| 115 | |
|---|
| 116 | |
|---|
| 117 | // at the very end of each profiler session, calc results and store to file |
|---|
| 118 | inline void PROFILER_STOP() { |
|---|
| 119 | |
|---|
| 120 | // first calc total runtimes |
|---|
| 121 | long TotalRunTime = CurrentTick() - FirstTick(); |
|---|
| 122 | long ProfiledRunTime = CurrentTick() - __PROFILER_GlobalStartTick; |
|---|
| 123 | |
|---|
| 124 | // calc max. filesize required... |
|---|
| 125 | long fileSize = 0; |
|---|
| 126 | fileSize = fileSize + 3 + 1 + 2; // Firmware Version + Enhanced flag |
|---|
| 127 | fileSize = fileSize + 4 + 2; // TotalRunTime |
|---|
| 128 | fileSize = fileSize + 4 + 2; // ProfiledRunTime |
|---|
| 129 | fileSize = fileSize + 4 + 2; // PROFILER_MAXSECTIONS |
|---|
| 130 | fileSize = PROFILER_MAXSECTIONS * (3 * 4 + 2); // profiler results + linebreak |
|---|
| 131 | |
|---|
| 132 | // needed to make everything clean etc. |
|---|
| 133 | string tmpStr1 = ""; |
|---|
| 134 | string tmpStr2 = ""; |
|---|
| 135 | string tmpStr3 = ""; |
|---|
| 136 | string tmpStr4 = ""; |
|---|
| 137 | long tmpLong = 0; |
|---|
| 138 | |
|---|
| 139 | // prepare file... |
|---|
| 140 | long bytesWritten = 0; |
|---|
| 141 | byte fid = 0; |
|---|
| 142 | CreateFile(PROFILER_RESULTSFILE, fileSize, fid); |
|---|
| 143 | |
|---|
| 144 | // start writing to file, line by line |
|---|
| 145 | // firmware version |
|---|
| 146 | tmpStr1 = NumToStr(__FIRMWARE_VERSION); |
|---|
| 147 | tmpStr2 = SubStr(tmpStr1, 0, 3); // truncate to 3 chars if needed |
|---|
| 148 | #ifdef __ENHANCED_FIRMWARE |
|---|
| 149 | tmpStr1 = StrCat(tmpStr2, "1"); // enhanced |
|---|
| 150 | #else |
|---|
| 151 | tmpStr1 = StrCat(tmpStr2, "0"); // standard |
|---|
| 152 | #endif |
|---|
| 153 | WriteLnString(fid, tmpStr1, bytesWritten); |
|---|
| 154 | |
|---|
| 155 | // total run time |
|---|
| 156 | tmpStr1 = Flatten(TotalRunTime); |
|---|
| 157 | WriteLnString(fid, tmpStr1, bytesWritten); |
|---|
| 158 | |
|---|
| 159 | // profiled run time |
|---|
| 160 | tmpStr1 = Flatten(ProfiledRunTime); |
|---|
| 161 | WriteLnString(fid, tmpStr1, bytesWritten); |
|---|
| 162 | |
|---|
| 163 | // PROFILER_MAXSECTIONS |
|---|
| 164 | tmpLong = PROFILER_MAXSECTIONS; |
|---|
| 165 | tmpStr1 = Flatten(tmpLong); |
|---|
| 166 | WriteLnString(fid, tmpStr1, bytesWritten); |
|---|
| 167 | |
|---|
| 168 | // now for every profile-section 1 line |
|---|
| 169 | for(int i = 0; i < PROFILER_MAXSECTIONS; i++) { |
|---|
| 170 | |
|---|
| 171 | // execution count, times no tick happened, elapsed ticks/ms |
|---|
| 172 | tmpStr1 = Flatten(__PROFILER_ExecutionCount[i]); |
|---|
| 173 | tmpStr2 = Flatten(__PROFILER_ExecutionsSameMs[i]); |
|---|
| 174 | tmpStr3 = Flatten(__PROFILER_ElapsedTicks[i]); |
|---|
| 175 | tmpStr4 = StrCat(tmpStr1, tmpStr2, tmpStr3); |
|---|
| 176 | |
|---|
| 177 | WriteLnString(fid, tmpStr4, bytesWritten); |
|---|
| 178 | }//end for |
|---|
| 179 | |
|---|
| 180 | CloseFile(fid); |
|---|
| 181 | |
|---|
| 182 | }//end void |
|---|
| 183 | |
|---|
| 184 | |
|---|
| 185 | #endif |
|---|
| 186 | |
|---|
| 187 | |
|---|
| 188 | |
|---|
| 189 | |
|---|
| 190 | |
|---|
| 191 | |
|---|
| 192 | // include empty main-task so we can check if this whole file compiles |
|---|
| 193 | #ifdef __LOCAL_COMPILE_FAKE_TEST |
|---|
| 194 | task main(){ |
|---|
| 195 | |
|---|
| 196 | }//end task |
|---|
| 197 | #endif |
|---|
| 198 | |
|---|
| 199 | |
|---|
| 200 | |
|---|
| 201 | #endif |
|---|
| 202 | |
|---|