SamplingTool.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. /*
  2. * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  14. * its contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  18. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  21. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. #include "config.h"
  29. #include "SamplingTool.h"
  30. #include "CodeBlock.h"
  31. #include "Interpreter.h"
  32. #include "Opcode.h"
  33. #if !OS(WINDOWS)
  34. #include <unistd.h>
  35. #endif
  36. namespace JSC {
  37. #if ENABLE(SAMPLING_FLAGS)
  38. void SamplingFlags::sample()
  39. {
  40. uint32_t mask = static_cast<uint32_t>(1 << 31);
  41. unsigned index;
  42. for (index = 0; index < 32; ++index) {
  43. if (mask & s_flags)
  44. break;
  45. mask >>= 1;
  46. }
  47. s_flagCounts[32 - index]++;
  48. }
  49. void SamplingFlags::start()
  50. {
  51. for (unsigned i = 0; i <= 32; ++i)
  52. s_flagCounts[i] = 0;
  53. }
  54. void SamplingFlags::stop()
  55. {
  56. uint64_t total = 0;
  57. for (unsigned i = 0; i <= 32; ++i)
  58. total += s_flagCounts[i];
  59. if (total) {
  60. dataLogF("\nSamplingFlags: sample counts with flags set: (%lld total)\n", total);
  61. for (unsigned i = 0; i <= 32; ++i) {
  62. if (s_flagCounts[i])
  63. dataLogF(" [ %02d ] : %lld\t\t(%03.2f%%)\n", i, s_flagCounts[i], (100.0 * s_flagCounts[i]) / total);
  64. }
  65. dataLogF("\n");
  66. } else
  67. dataLogF("\nSamplingFlags: no samples.\n\n");
  68. }
  69. uint64_t SamplingFlags::s_flagCounts[33];
  70. #else
  71. void SamplingFlags::start() {}
  72. void SamplingFlags::stop() {}
  73. #endif
  74. #if ENABLE(SAMPLING_REGIONS)
  75. volatile uintptr_t SamplingRegion::s_currentOrReserved;
  76. Spectrum<const char*>* SamplingRegion::s_spectrum;
  77. unsigned long SamplingRegion::s_noneOfTheAbove;
  78. unsigned SamplingRegion::s_numberOfSamplesSinceDump;
  79. SamplingRegion::Locker::Locker()
  80. {
  81. uintptr_t previous;
  82. while (true) {
  83. previous = s_currentOrReserved;
  84. if (previous & 1) {
  85. #if OS(UNIX)
  86. sched_yield();
  87. #endif
  88. continue;
  89. }
  90. if (WTF::weakCompareAndSwapUIntPtr(&s_currentOrReserved, previous, previous | 1))
  91. break;
  92. }
  93. }
  94. SamplingRegion::Locker::~Locker()
  95. {
  96. // We don't need the CAS, but we do it out of an
  97. // abundance of caution (and because it gives us a memory fence, which is
  98. // never bad).
  99. uintptr_t previous;
  100. do {
  101. previous = s_currentOrReserved;
  102. } while (!WTF::weakCompareAndSwapUIntPtr(&s_currentOrReserved, previous, previous & ~1));
  103. }
  104. void SamplingRegion::sample()
  105. {
  106. // Make sure we lock s_current.
  107. Locker locker;
  108. // Create a spectrum if we don't have one already.
  109. if (!s_spectrum)
  110. s_spectrum = new Spectrum<const char*>();
  111. ASSERT(s_currentOrReserved & 1);
  112. // Walk the region stack, and record each region we see.
  113. SamplingRegion* region = bitwise_cast<SamplingRegion*>(s_currentOrReserved & ~1);
  114. if (region) {
  115. for (; region; region = region->m_previous)
  116. s_spectrum->add(region->m_name);
  117. } else
  118. s_noneOfTheAbove++;
  119. if (s_numberOfSamplesSinceDump++ == SamplingThread::s_hertz) {
  120. s_numberOfSamplesSinceDump = 0;
  121. dumpInternal();
  122. }
  123. }
  124. void SamplingRegion::dump()
  125. {
  126. Locker locker;
  127. dumpInternal();
  128. }
  129. void SamplingRegion::dumpInternal()
  130. {
  131. if (!s_spectrum) {
  132. dataLogF("\nSamplingRegion: was never sampled.\n\n");
  133. return;
  134. }
  135. Vector<Spectrum<const char*>::KeyAndCount> list = s_spectrum->buildList();
  136. unsigned long total = s_noneOfTheAbove;
  137. for (unsigned i = list.size(); i--;)
  138. total += list[i].count;
  139. dataLogF("\nSamplingRegion: sample counts for regions: (%lu samples)\n", total);
  140. for (unsigned i = list.size(); i--;)
  141. dataLogF(" %3.2lf%% %s\n", (100.0 * list[i].count) / total, list[i].key);
  142. }
  143. #else // ENABLE(SAMPLING_REGIONS)
  144. void SamplingRegion::dump() { }
  145. #endif // ENABLE(SAMPLING_REGIONS)
  146. /*
  147. Start with flag 16 set.
  148. By doing this the monitoring of lower valued flags will be masked out
  149. until flag 16 is explictly cleared.
  150. */
  151. uint32_t SamplingFlags::s_flags = 1 << 15;
  152. #if OS(WINDOWS)
  153. static void sleepForMicroseconds(unsigned us)
  154. {
  155. unsigned ms = us / 1000;
  156. if (us && !ms)
  157. ms = 1;
  158. Sleep(ms);
  159. }
  160. #else
  161. static void sleepForMicroseconds(unsigned us)
  162. {
  163. usleep(us);
  164. }
  165. #endif
  166. static inline unsigned hertz2us(unsigned hertz)
  167. {
  168. return 1000000 / hertz;
  169. }
  170. SamplingTool* SamplingTool::s_samplingTool = 0;
  171. bool SamplingThread::s_running = false;
  172. unsigned SamplingThread::s_hertz = 10000;
  173. ThreadIdentifier SamplingThread::s_samplingThread;
  174. void SamplingThread::threadStartFunc(void*)
  175. {
  176. while (s_running) {
  177. sleepForMicroseconds(hertz2us(s_hertz));
  178. #if ENABLE(SAMPLING_FLAGS)
  179. SamplingFlags::sample();
  180. #endif
  181. #if ENABLE(SAMPLING_REGIONS)
  182. SamplingRegion::sample();
  183. #endif
  184. #if ENABLE(OPCODE_SAMPLING)
  185. SamplingTool::sample();
  186. #endif
  187. }
  188. }
  189. void SamplingThread::start(unsigned hertz)
  190. {
  191. ASSERT(!s_running);
  192. s_running = true;
  193. s_hertz = hertz;
  194. s_samplingThread = createThread(threadStartFunc, 0, "JavaScriptCore::Sampler");
  195. }
  196. void SamplingThread::stop()
  197. {
  198. ASSERT(s_running);
  199. s_running = false;
  200. waitForThreadCompletion(s_samplingThread);
  201. }
  202. void ScriptSampleRecord::sample(CodeBlock* codeBlock, Instruction* vPC)
  203. {
  204. if (!m_samples) {
  205. m_size = codeBlock->instructions().size();
  206. m_samples = static_cast<int*>(calloc(m_size, sizeof(int)));
  207. m_codeBlock = codeBlock;
  208. }
  209. ++m_sampleCount;
  210. unsigned offest = vPC - codeBlock->instructions().begin();
  211. // Since we don't read and write codeBlock and vPC atomically, this check
  212. // can fail if we sample mid op_call / op_ret.
  213. if (offest < m_size) {
  214. m_samples[offest]++;
  215. m_opcodeSampleCount++;
  216. }
  217. }
  218. void SamplingTool::doRun()
  219. {
  220. Sample sample(m_sample, m_codeBlock);
  221. ++m_sampleCount;
  222. if (sample.isNull())
  223. return;
  224. if (!sample.inHostFunction()) {
  225. unsigned opcodeID = m_interpreter->getOpcodeID(sample.vPC()[0].u.opcode);
  226. ++m_opcodeSampleCount;
  227. ++m_opcodeSamples[opcodeID];
  228. if (sample.inCTIFunction())
  229. m_opcodeSamplesInCTIFunctions[opcodeID]++;
  230. }
  231. #if ENABLE(CODEBLOCK_SAMPLING)
  232. if (CodeBlock* codeBlock = sample.codeBlock()) {
  233. MutexLocker locker(m_scriptSampleMapMutex);
  234. ScriptSampleRecord* record = m_scopeSampleMap->get(codeBlock->ownerExecutable());
  235. ASSERT(record);
  236. record->sample(codeBlock, sample.vPC());
  237. }
  238. #endif
  239. }
  240. void SamplingTool::sample()
  241. {
  242. s_samplingTool->doRun();
  243. }
  244. void SamplingTool::notifyOfScope(VM& vm, ScriptExecutable* script)
  245. {
  246. #if ENABLE(CODEBLOCK_SAMPLING)
  247. MutexLocker locker(m_scriptSampleMapMutex);
  248. m_scopeSampleMap->set(script, adoptPtr(new ScriptSampleRecord(vm, script)));
  249. #else
  250. UNUSED_PARAM(vm);
  251. UNUSED_PARAM(script);
  252. #endif
  253. }
  254. void SamplingTool::setup()
  255. {
  256. s_samplingTool = this;
  257. }
  258. #if ENABLE(OPCODE_SAMPLING)
  259. struct OpcodeSampleInfo {
  260. OpcodeID opcode;
  261. long long count;
  262. long long countInCTIFunctions;
  263. };
  264. struct LineCountInfo {
  265. unsigned line;
  266. unsigned count;
  267. };
  268. static int compareOpcodeIndicesSampling(const void* left, const void* right)
  269. {
  270. const OpcodeSampleInfo* leftSampleInfo = reinterpret_cast<const OpcodeSampleInfo*>(left);
  271. const OpcodeSampleInfo* rightSampleInfo = reinterpret_cast<const OpcodeSampleInfo*>(right);
  272. return (leftSampleInfo->count < rightSampleInfo->count) ? 1 : (leftSampleInfo->count > rightSampleInfo->count) ? -1 : 0;
  273. }
  274. #if ENABLE(CODEBLOCK_SAMPLING)
  275. static int compareLineCountInfoSampling(const void* left, const void* right)
  276. {
  277. const LineCountInfo* leftLineCount = reinterpret_cast<const LineCountInfo*>(left);
  278. const LineCountInfo* rightLineCount = reinterpret_cast<const LineCountInfo*>(right);
  279. return (leftLineCount->line > rightLineCount->line) ? 1 : (leftLineCount->line < rightLineCount->line) ? -1 : 0;
  280. }
  281. static int compareScriptSampleRecords(const void* left, const void* right)
  282. {
  283. const ScriptSampleRecord* const leftValue = *static_cast<const ScriptSampleRecord* const *>(left);
  284. const ScriptSampleRecord* const rightValue = *static_cast<const ScriptSampleRecord* const *>(right);
  285. return (leftValue->m_sampleCount < rightValue->m_sampleCount) ? 1 : (leftValue->m_sampleCount > rightValue->m_sampleCount) ? -1 : 0;
  286. }
  287. #endif
  288. void SamplingTool::dump(ExecState* exec)
  289. {
  290. // Tidies up SunSpider output by removing short scripts - such a small number of samples would likely not be useful anyhow.
  291. if (m_sampleCount < 10)
  292. return;
  293. // (1) Build and sort 'opcodeSampleInfo' array.
  294. OpcodeSampleInfo opcodeSampleInfo[numOpcodeIDs];
  295. for (int i = 0; i < numOpcodeIDs; ++i) {
  296. opcodeSampleInfo[i].opcode = static_cast<OpcodeID>(i);
  297. opcodeSampleInfo[i].count = m_opcodeSamples[i];
  298. opcodeSampleInfo[i].countInCTIFunctions = m_opcodeSamplesInCTIFunctions[i];
  299. }
  300. qsort(opcodeSampleInfo, numOpcodeIDs, sizeof(OpcodeSampleInfo), compareOpcodeIndicesSampling);
  301. // (2) Print Opcode sampling results.
  302. dataLogF("\nBytecode samples [*]\n");
  303. dataLogF(" sample %% of %% of | cti cti %%\n");
  304. dataLogF("opcode count VM total | count of self\n");
  305. dataLogF("------------------------------------------------------- | ----------------\n");
  306. for (int i = 0; i < numOpcodeIDs; ++i) {
  307. long long count = opcodeSampleInfo[i].count;
  308. if (!count)
  309. continue;
  310. OpcodeID opcodeID = opcodeSampleInfo[i].opcode;
  311. const char* opcodeName = opcodeNames[opcodeID];
  312. const char* opcodePadding = padOpcodeName(opcodeID, 28);
  313. double percentOfVM = (static_cast<double>(count) * 100) / m_opcodeSampleCount;
  314. double percentOfTotal = (static_cast<double>(count) * 100) / m_sampleCount;
  315. long long countInCTIFunctions = opcodeSampleInfo[i].countInCTIFunctions;
  316. double percentInCTIFunctions = (static_cast<double>(countInCTIFunctions) * 100) / count;
  317. debugDebugPrintf("%s:%s%-6lld %.3f%%\t%.3f%%\t | %-6lld %.3f%%\n", opcodeName, opcodePadding, count, percentOfVM, percentOfTotal, countInCTIFunctions, percentInCTIFunctions);
  318. }
  319. dataLogF("\n[*] Samples inside host code are not charged to any Bytecode.\n\n");
  320. dataLogF("\tSamples inside VM:\t\t%lld / %lld (%.3f%%)\n", m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_opcodeSampleCount) * 100) / m_sampleCount);
  321. dataLogF("\tSamples inside host code:\t%lld / %lld (%.3f%%)\n\n", m_sampleCount - m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_sampleCount - m_opcodeSampleCount) * 100) / m_sampleCount);
  322. dataLogF("\tsample count:\tsamples inside this opcode\n");
  323. dataLogF("\t%% of VM:\tsample count / all opcode samples\n");
  324. dataLogF("\t%% of total:\tsample count / all samples\n");
  325. dataLogF("\t--------------\n");
  326. dataLogF("\tcti count:\tsamples inside a CTI function called by this opcode\n");
  327. dataLogF("\tcti %% of self:\tcti count / sample count\n");
  328. #if ENABLE(CODEBLOCK_SAMPLING)
  329. // (3) Build and sort 'codeBlockSamples' array.
  330. int scopeCount = m_scopeSampleMap->size();
  331. Vector<ScriptSampleRecord*> codeBlockSamples(scopeCount);
  332. ScriptSampleRecordMap::iterator iter = m_scopeSampleMap->begin();
  333. for (int i = 0; i < scopeCount; ++i, ++iter)
  334. codeBlockSamples[i] = iter->value.get();
  335. qsort(codeBlockSamples.begin(), scopeCount, sizeof(ScriptSampleRecord*), compareScriptSampleRecords);
  336. // (4) Print data from 'codeBlockSamples' array.
  337. dataLogF("\nCodeBlock samples\n\n");
  338. for (int i = 0; i < scopeCount; ++i) {
  339. ScriptSampleRecord* record = codeBlockSamples[i];
  340. CodeBlock* codeBlock = record->m_codeBlock;
  341. double blockPercent = (record->m_sampleCount * 100.0) / m_sampleCount;
  342. if (blockPercent >= 1) {
  343. //Instruction* code = codeBlock->instructions().begin();
  344. dataLogF("#%d: %s:%d: %d / %lld (%.3f%%)\n", i + 1, record->m_executable->sourceURL().utf8().data(), codeBlock->lineNumberForBytecodeOffset(0), record->m_sampleCount, m_sampleCount, blockPercent);
  345. if (i < 10) {
  346. HashMap<unsigned,unsigned> lineCounts;
  347. codeBlock->dump(exec);
  348. dataLogF(" Opcode and line number samples [*]\n\n");
  349. for (unsigned op = 0; op < record->m_size; ++op) {
  350. int count = record->m_samples[op];
  351. if (count) {
  352. dataLogF(" [% 4d] has sample count: % 4d\n", op, count);
  353. unsigned line = codeBlock->lineNumberForBytecodeOffset(op);
  354. lineCounts.set(line, (lineCounts.contains(line) ? lineCounts.get(line) : 0) + count);
  355. }
  356. }
  357. dataLogF("\n");
  358. int linesCount = lineCounts.size();
  359. Vector<LineCountInfo> lineCountInfo(linesCount);
  360. int lineno = 0;
  361. for (HashMap<unsigned,unsigned>::iterator iter = lineCounts.begin(); iter != lineCounts.end(); ++iter, ++lineno) {
  362. lineCountInfo[lineno].line = iter->key;
  363. lineCountInfo[lineno].count = iter->value;
  364. }
  365. qsort(lineCountInfo.begin(), linesCount, sizeof(LineCountInfo), compareLineCountInfoSampling);
  366. for (lineno = 0; lineno < linesCount; ++lineno) {
  367. dataLogF(" Line #%d has sample count %d.\n", lineCountInfo[lineno].line, lineCountInfo[lineno].count);
  368. }
  369. dataLogF("\n");
  370. dataLogF(" [*] Samples inside host code are charged to the calling Bytecode.\n");
  371. dataLogF(" Samples on a call / return boundary are not charged to a specific opcode or line.\n\n");
  372. dataLogF(" Samples on a call / return boundary: %d / %d (%.3f%%)\n\n", record->m_sampleCount - record->m_opcodeSampleCount, record->m_sampleCount, (static_cast<double>(record->m_sampleCount - record->m_opcodeSampleCount) * 100) / record->m_sampleCount);
  373. }
  374. }
  375. }
  376. #else
  377. UNUSED_PARAM(exec);
  378. #endif
  379. }
  380. #else
  381. void SamplingTool::dump(ExecState*)
  382. {
  383. }
  384. #endif
  385. } // namespace JSC