coverage.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. "use strict";
  2. const is_env_node = v86util.check_env_node();
  3. const fs = is_env_node && require("fs");
  4. const path = is_env_node && require("path");
  5. /** @constructor */
  6. function CoverageLogger()
  7. {
  8. this.ENABLED = COVERAGE_LOGGER_ALLOW && DEBUG && is_env_node;
  9. if(!this.ENABLED)
  10. {
  11. return;
  12. }
  13. this.should_log_coverage = false;
  14. this.memory_base = 0;
  15. }
  16. CoverageLogger.prototype.log_start = function()
  17. {
  18. if(!this.ENABLED)
  19. {
  20. return;
  21. }
  22. this.should_log_coverage = true;
  23. };
  24. CoverageLogger.prototype.log_end = function()
  25. {
  26. if(!this.ENABLED)
  27. {
  28. return;
  29. }
  30. this.should_log_coverage = false;
  31. };
  32. CoverageLogger.prototype.init = function(wm)
  33. {
  34. if(!this.ENABLED)
  35. {
  36. return;
  37. }
  38. this.coverage_map = {};
  39. this.memory_base = wm.imports.env["memoryBase"];
  40. this.COVERAGE_DATA_PATH = path.join(__dirname, "coverage");
  41. for(let name of Object.keys(wm.exports))
  42. {
  43. if(name.startsWith(COVERAGE_EXPORT_PREFIX))
  44. {
  45. const fn_id = wm.exports[name];
  46. this.coverage_map[fn_id] = {};
  47. const coverage_data = this.coverage_map[fn_id];
  48. // fn_id -> func_name
  49. coverage_data.fn_name = name.slice(COVERAGE_EXPORT_PREFIX.length);
  50. // fn_id -> block_covered ([0,2,3])
  51. coverage_data.visit_logs = new Uint8Array(8);
  52. // Position within visit_logs from where to append data
  53. coverage_data.pos = 0;
  54. // Total number of conditional blocks in fn_id
  55. coverage_data.total_blocks = 0;
  56. }
  57. }
  58. };
  59. CoverageLogger.prototype.log = function(fn_name_offset, num_blocks, visited_block)
  60. {
  61. if(!this.ENABLED || !this.should_log_coverage)
  62. {
  63. return;
  64. }
  65. // fn_name_offset is an offset in the data section to where the string of the function
  66. // name is stored, so it varies by memoryBase, whereas the __profn exported fn_id doesn't
  67. const fn_id = fn_name_offset - this.memory_base;
  68. const coverage_data = this.coverage_map[fn_id];
  69. if(!coverage_data)
  70. {
  71. // Static functions may not be "discovered" in coverage_init - we currently simply
  72. // skip them
  73. return;
  74. }
  75. const log_pos = coverage_data.pos;
  76. const existing_entry = coverage_data.visit_logs.indexOf(visited_block);
  77. if((existing_entry > -1 && existing_entry < log_pos) || num_blocks > 0xFF)
  78. {
  79. // If we'd like to profile frequency of code visited, we should be using counters
  80. // instead. This approach was simply faster to measure coverage.
  81. return;
  82. }
  83. coverage_data.total_blocks = num_blocks;
  84. coverage_data.visit_logs[log_pos] = visited_block;
  85. coverage_data.pos++;
  86. if(log_pos >= coverage_data.visit_logs.length - 1)
  87. {
  88. this.dump_to_files();
  89. coverage_data.pos = 0;
  90. }
  91. };
  92. CoverageLogger.prototype.dump_to_files = function()
  93. {
  94. if(!this.ENABLED)
  95. {
  96. return;
  97. }
  98. for(let fn_id of Object.keys(this.coverage_map))
  99. {
  100. const coverage_data = this.coverage_map[fn_id];
  101. if(coverage_data.pos)
  102. {
  103. const fn_name = coverage_data.fn_name;
  104. const total_blocks = coverage_data.total_blocks;
  105. const filename = path.join(
  106. this.COVERAGE_DATA_PATH,
  107. `${COVERAGE_FILE_PREFIX}_${fn_name}_${total_blocks}`
  108. );
  109. // XXX: Experiment more with async I/O - preliminarily it seemed to choke the nasm test
  110. // even when limiting max files open simultaneously
  111. fs["appendFileSync"](
  112. filename,
  113. Buffer.from(coverage_data.visit_logs.buffer, 0, coverage_data.pos)
  114. );
  115. coverage_data.pos = 0;
  116. }
  117. }
  118. };