| 139 | | |
|---|
| 140 | | #if SAMPLING_TOOL_ENABLED |
|---|
| 141 | | |
|---|
| 142 | | void ScopeSampleRecord::sample(CodeBlock* codeBlock, Instruction* vPC) |
|---|
| 143 | | { |
|---|
| 144 | | m_totalCount++; |
|---|
| 145 | | |
|---|
| 146 | | if (!m_vpcCounts) { |
|---|
| 147 | | m_size = codeBlock->instructions.size(); |
|---|
| 148 | | m_vpcCounts = static_cast<int*>(calloc(m_size, sizeof(int))); |
|---|
| 149 | | m_codeBlock = codeBlock; |
|---|
| 150 | | } |
|---|
| 151 | | |
|---|
| 152 | | unsigned codeOffset = static_cast<unsigned>(reinterpret_cast<ptrdiff_t>(vPC) - reinterpret_cast<ptrdiff_t>(codeBlock->instructions.begin())) / sizeof(Instruction*); |
|---|
| 153 | | // This could occur if codeBlock & vPC are not consistent - e.g. sample mid op_call/op_ret. |
|---|
| 154 | | if (codeOffset < m_size) |
|---|
| 155 | | m_vpcCounts[codeOffset]++; |
|---|
| 156 | | } |
|---|
| 157 | | |
|---|
| 158 | | static inline unsigned hertz2us(unsigned hertz) |
|---|
| 159 | | { |
|---|
| 160 | | return 1000000 / hertz; |
|---|
| 161 | | } |
|---|
| 162 | | |
|---|
| 163 | | void SamplingTool::run() |
|---|
| 164 | | { |
|---|
| 165 | | while (m_running) { |
|---|
| 166 | | usleep(hertz2us(m_hertz)); |
|---|
| 167 | | |
|---|
| 168 | | m_totalSamples++; |
|---|
| 169 | | |
|---|
| 170 | | CodeBlock* codeBlock = m_recordedCodeBlock; |
|---|
| 171 | | Instruction* vPC = m_recordedVPC; |
|---|
| 172 | | |
|---|
| 173 | | if (codeBlock && vPC) { |
|---|
| 174 | | ScopeSampleRecord* record = m_scopeSampleMap->get(codeBlock->ownerNode); |
|---|
| 175 | | if (record) |
|---|
| 176 | | record->sample(codeBlock, vPC); |
|---|
| 177 | | } |
|---|
| 178 | | } |
|---|
| 179 | | } |
|---|
| 180 | | |
|---|
| 181 | | void* SamplingTool::threadStartFunc(void* samplingTool) |
|---|
| 182 | | { |
|---|
| 183 | | reinterpret_cast<SamplingTool*>(samplingTool)->run(); |
|---|
| 184 | | return 0; |
|---|
| 185 | | } |
|---|
| 186 | | |
|---|
| 187 | | void SamplingTool::notifyOfScope(ScopeNode* scope) |
|---|
| 188 | | { |
|---|
| 189 | | m_scopeSampleMap->set(scope, new ScopeSampleRecord(scope)); |
|---|
| 190 | | } |
|---|
| 191 | | |
|---|
| 192 | | void SamplingTool::start(unsigned hertz) |
|---|
| 193 | | { |
|---|
| 194 | | ASSERT(!m_running); |
|---|
| 195 | | m_running = true; |
|---|
| 196 | | m_hertz = hertz; |
|---|
| 197 | | pthread_create(&m_samplingThread, 0, threadStartFunc, this); |
|---|
| 198 | | } |
|---|
| 199 | | |
|---|
| 200 | | void SamplingTool::stop() |
|---|
| 201 | | { |
|---|
| 202 | | ASSERT(m_running); |
|---|
| 203 | | m_running = false; |
|---|
| 204 | | pthread_join(m_samplingThread, 0); |
|---|
| 205 | | } |
|---|
| 206 | | |
|---|
| 207 | | struct OpcodeSampleInfo |
|---|
| 208 | | { |
|---|
| 209 | | OpcodeID opcode; |
|---|
| 210 | | long long count; |
|---|
| 211 | | }; |
|---|
| 212 | | |
|---|
| 213 | | struct LineCountInfo |
|---|
| 214 | | { |
|---|
| 215 | | unsigned line; |
|---|
| 216 | | unsigned count; |
|---|
| 217 | | }; |
|---|
| 218 | | |
|---|
| 219 | | static int compareLineCountInfoSampling(const void* left, const void* right) |
|---|
| 220 | | { |
|---|
| 221 | | const LineCountInfo *leftLineCount = reinterpret_cast<const LineCountInfo *>(left); |
|---|
| 222 | | const LineCountInfo *rightLineCount = reinterpret_cast<const LineCountInfo *>(right); |
|---|
| 223 | | |
|---|
| 224 | | return (leftLineCount->line > rightLineCount->line) ? 1 : (leftLineCount->line < rightLineCount->line) ? -1 : 0; |
|---|
| 225 | | } |
|---|
| 226 | | |
|---|
| 227 | | static int compareOpcodeIndicesSampling(const void* left, const void* right) |
|---|
| 228 | | { |
|---|
| 229 | | const OpcodeSampleInfo *leftSampleInfo = reinterpret_cast<const OpcodeSampleInfo *>(left); |
|---|
| 230 | | const OpcodeSampleInfo *rightSampleInfo = reinterpret_cast<const OpcodeSampleInfo *>(right); |
|---|
| 231 | | |
|---|
| 232 | | return (leftSampleInfo->count < rightSampleInfo->count) ? 1 : (leftSampleInfo->count > rightSampleInfo->count) ? -1 : 0; |
|---|
| 233 | | } |
|---|
| 234 | | |
|---|
| 235 | | static int compareScopeSampleRecords(const void* left, const void* right) |
|---|
| 236 | | { |
|---|
| 237 | | const ScopeSampleRecord* const leftValue = *static_cast<const ScopeSampleRecord* const *>(left); |
|---|
| 238 | | const ScopeSampleRecord* const rightValue = *static_cast<const ScopeSampleRecord* const *>(right); |
|---|
| 239 | | |
|---|
| 240 | | return (leftValue->m_totalCount < rightValue->m_totalCount) ? 1 : (leftValue->m_totalCount > rightValue->m_totalCount) ? -1 : 0; |
|---|
| 241 | | } |
|---|
| 242 | | |
|---|
| 243 | | void SamplingTool::dump(ExecState* exec) |
|---|
| 244 | | { |
|---|
| 245 | | // Tidies up SunSpider output by removing short scripts - such a small number of samples would likely not be useful anyhow. |
|---|
| 246 | | if (m_totalSamples < 10) |
|---|
| 247 | | return; |
|---|
| 248 | | |
|---|
| 249 | | // (1) Calculate 'totalCodeBlockSamples', build and sort 'codeBlockSamples' array. |
|---|
| 250 | | |
|---|
| 251 | | int scopeCount = m_scopeSampleMap->size(); |
|---|
| 252 | | long long totalCodeBlockSamples = 0; |
|---|
| 253 | | ScopeSampleRecord* codeBlockSamples[scopeCount]; |
|---|
| 254 | | ScopeSampleRecordMap::iterator iter = m_scopeSampleMap->begin(); |
|---|
| 255 | | for (int i=0; i < scopeCount; ++i, ++iter) { |
|---|
| 256 | | codeBlockSamples[i] = iter->second; |
|---|
| 257 | | totalCodeBlockSamples += codeBlockSamples[i]->m_totalCount; |
|---|
| 258 | | } |
|---|
| 259 | | mergesort(codeBlockSamples, scopeCount, sizeof(ScopeSampleRecord*), compareScopeSampleRecords); |
|---|
| 260 | | |
|---|
| 261 | | // (2) Print data from 'codeBlockSamples' array, calculate 'totalOpcodeSamples', populate 'opcodeSampleCounts' array. |
|---|
| 262 | | |
|---|
| 263 | | long long totalOpcodeSamples = 0; |
|---|
| 264 | | long long opcodeSampleCounts[numOpcodeIDs] = { 0 }; |
|---|
| 265 | | |
|---|
| 266 | | fprintf(stdout, "\nBlock sampling results\n\n"); |
|---|
| 267 | | fprintf(stdout, "Total blocks sampled (total samples): %lld (%lld)\n\n", totalCodeBlockSamples, m_totalSamples); |
|---|
| 268 | | |
|---|
| 269 | | for (int i=0; i < scopeCount; i++) { |
|---|
| 270 | | ScopeSampleRecord *record = codeBlockSamples[i]; |
|---|
| 271 | | CodeBlock* codeBlock = record->m_codeBlock; |
|---|
| 272 | | |
|---|
| 273 | | double totalPercent = (record->m_totalCount * 100.0)/m_totalSamples; |
|---|
| 274 | | double blockPercent = (record->m_totalCount * 100.0)/totalCodeBlockSamples; |
|---|
| 275 | | |
|---|
| 276 | | if ((blockPercent >= 1) && codeBlock) { |
|---|
| 277 | | Instruction* code = codeBlock->instructions.begin(); |
|---|
| 278 | | fprintf(stdout, "#%d: %s:%d: sampled %d times - %.3f%% (%.3f%%)\n", i+1, record->m_scope->sourceURL().UTF8String().c_str(), codeBlock->lineNumberForVPC(code), record->m_totalCount, blockPercent, totalPercent); |
|---|
| 279 | | if (i < 10) { |
|---|
| 280 | | HashMap<unsigned,unsigned> lineCounts; |
|---|
| 281 | | codeBlock->dump(exec); |
|---|
| 282 | | for (unsigned op = 0; op < record->m_size; ++op) { |
|---|
| 283 | | int count = record->m_vpcCounts[op]; |
|---|
| 284 | | if (count) { |
|---|
| 285 | | printf(" [% 4d] has sample count: % 4d\n", op, count); |
|---|
| 286 | | unsigned line = codeBlock->lineNumberForVPC(code+op); |
|---|
| 287 | | lineCounts.set(line, (lineCounts.contains(line) ? lineCounts.get(line) : 0) + count); |
|---|
| 288 | | } |
|---|
| 289 | | } |
|---|
| 290 | | printf("\n"); |
|---|
| 291 | | int linesCount = lineCounts.size(); |
|---|
| 292 | | LineCountInfo lineCountInfo[linesCount]; |
|---|
| 293 | | int lineno = 0; |
|---|
| 294 | | for (HashMap<unsigned,unsigned>::iterator iter = lineCounts.begin(); iter != lineCounts.end(); ++iter, ++lineno) { |
|---|
| 295 | | lineCountInfo[lineno].line = iter->first; |
|---|
| 296 | | lineCountInfo[lineno].count = iter->second; |
|---|
| 297 | | } |
|---|
| 298 | | mergesort(lineCountInfo, linesCount, sizeof(LineCountInfo), compareLineCountInfoSampling); |
|---|
| 299 | | for (lineno = 0; lineno < linesCount; ++lineno) { |
|---|
| 300 | | printf(" Line #%d has sample count %d.\n", lineCountInfo[lineno].line, lineCountInfo[lineno].count); |
|---|
| 301 | | } |
|---|
| 302 | | printf("\n"); |
|---|
| 303 | | } |
|---|
| 304 | | } |
|---|
| 305 | | |
|---|
| 306 | | if (record->m_vpcCounts && codeBlock) { |
|---|
| 307 | | Instruction* instructions = codeBlock->instructions.begin(); |
|---|
| 308 | | for (unsigned op = 0; op < record->m_size; ++op) { |
|---|
| 309 | | Opcode opcode = instructions[op].u.opcode; |
|---|
| 310 | | if (exec->machine()->isOpcode(opcode)) { |
|---|
| 311 | | totalOpcodeSamples += record->m_vpcCounts[op]; |
|---|
| 312 | | opcodeSampleCounts[exec->machine()->getOpcodeID(opcode)] += record->m_vpcCounts[op]; |
|---|
| 313 | | } |
|---|
| 314 | | } |
|---|
| 315 | | } |
|---|
| 316 | | } |
|---|
| 317 | | printf("\n"); |
|---|
| 318 | | |
|---|
| 319 | | // (3) Build and sort 'opcodeSampleInfo' array. |
|---|
| 320 | | |
|---|
| 321 | | OpcodeSampleInfo opcodeSampleInfo[numOpcodeIDs]; |
|---|
| 322 | | for (int i = 0; i < numOpcodeIDs; ++i) { |
|---|
| 323 | | opcodeSampleInfo[i].opcode = (OpcodeID)i; |
|---|
| 324 | | opcodeSampleInfo[i].count = opcodeSampleCounts[i]; |
|---|
| 325 | | } |
|---|
| 326 | | mergesort(opcodeSampleInfo, numOpcodeIDs, sizeof(OpcodeSampleInfo), compareOpcodeIndicesSampling); |
|---|
| 327 | | |
|---|
| 328 | | // (4) Print Opcode sampling results. |
|---|
| 329 | | |
|---|
| 330 | | fprintf(stdout, "\nOpcode sampling results\n\n"); |
|---|
| 331 | | |
|---|
| 332 | | fprintf(stdout, "Total opcodes sampled (total samples): %lld (%lld)\n\n", totalOpcodeSamples, m_totalSamples); |
|---|
| 333 | | fprintf(stdout, "Opcodes in order:\n\n"); |
|---|
| 334 | | for (int i = 0; i < numOpcodeIDs; ++i) { |
|---|
| 335 | | long long count = opcodeSampleCounts[i]; |
|---|
| 336 | | fprintf(stdout, "%s:\t%6lld\t%.3f%%\t(%.3f%%)\n", opcodeNames[i], count, ((double)count * 100)/totalOpcodeSamples, ((double)count * 100)/m_totalSamples); |
|---|
| 337 | | } |
|---|
| 338 | | fprintf(stdout, "\n"); |
|---|
| 339 | | fprintf(stdout, "Opcodes by sample count:\n\n"); |
|---|
| 340 | | for (int i = 0; i < numOpcodeIDs; ++i) { |
|---|
| 341 | | OpcodeID opcode = opcodeSampleInfo[i].opcode; |
|---|
| 342 | | long long count = opcodeSampleInfo[i].count; |
|---|
| 343 | | fprintf(stdout, "%s:\t%6lld\t%.3f%%\t(%.3f%%)\n", opcodeNames[opcode], count, ((double)count * 100)/totalOpcodeSamples, ((double)count * 100)/m_totalSamples); |
|---|
| 344 | | } |
|---|
| 345 | | fprintf(stdout, "\n"); |
|---|
| 346 | | } |
|---|
| 347 | | |
|---|
| 348 | | #endif |
|---|
| 349 | | |
|---|