memory_tracker.cc 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #include "memory_tracker.h"
  2. #include "simulator.h"
  3. #include "thread_manager.h"
  4. #include "thread.h"
  5. #include <unordered_set>
  6. MemoryTracker::MemoryTracker()
  7. {
  8. Sim()->getConfig()->setCacheEfficiencyCallbacks(__ce_get_owner, __ce_notify_access, __ce_notify_evict, (UInt64)this);
  9. }
  10. MemoryTracker::~MemoryTracker()
  11. {
  12. FILE *fp = fopen(Sim()->getConfig()->formatOutputFileName("sim.memorytracker").c_str(), "w");
  13. std::unordered_set<UInt64> sites_printed;
  14. fprintf(fp, "W\t");
  15. for(int h = HitWhere::WHERE_FIRST ; h < HitWhere::NUM_HITWHERES ; h++)
  16. if (HitWhereIsValid((HitWhere::where_t)h))
  17. fprintf(fp, "%s,", HitWhereString((HitWhere::where_t)h));
  18. fprintf(fp, "\n");
  19. for(auto it = m_allocation_sites.begin(); it != m_allocation_sites.end(); ++it)
  20. {
  21. const CallStack &stack = it->first;
  22. const AllocationSite *site = it->second;
  23. if (site->total_loads + site->total_stores)
  24. {
  25. for(auto jt = stack.begin(); jt != stack.end(); ++jt)
  26. {
  27. if (sites_printed.count(*jt) == 0)
  28. {
  29. const RoutineTracer::Routine *rtn = Sim()->getRoutineTracer()->getRoutineInfo(*jt);
  30. if (rtn)
  31. fprintf(fp, "F\t%" PRIxPTR "\t%s\t%s\n", *jt, rtn->m_name, rtn->m_location);
  32. sites_printed.insert(*jt);
  33. }
  34. }
  35. fprintf(fp, "S\t%lx\t", (unsigned long)site);
  36. for(auto jt = stack.begin(); jt != stack.end(); ++jt)
  37. fprintf(fp, ":%" PRIxPTR, *jt);
  38. fprintf(fp, "\tnum-allocations=%" PRId64, site->num_allocations);
  39. fprintf(fp, "\ttotal-allocated=%" PRId64, site->total_size);
  40. fprintf(fp, "\thit-where=");
  41. for(int h = HitWhere::WHERE_FIRST ; h < HitWhere::NUM_HITWHERES ; h++)
  42. {
  43. if (HitWhereIsValid((HitWhere::where_t)h) && site->hit_where_load[h] + site->hit_where_store[h] > 0)
  44. {
  45. fprintf(fp, "L%s:%" PRId64 ",", HitWhereString((HitWhere::where_t)h), site->hit_where_load[h]);
  46. fprintf(fp, "S%s:%" PRId64 ",", HitWhereString((HitWhere::where_t)h), site->hit_where_store[h]);
  47. }
  48. }
  49. if (site->evicted_by.size())
  50. {
  51. fprintf(fp, "\tevicted-by=");
  52. for(auto it = site->evicted_by.begin(); it != site->evicted_by.end(); ++it)
  53. fprintf(fp, "%lx:%" PRId64 ",", (unsigned long)it->first, it->second);
  54. }
  55. fprintf(fp, "\n");
  56. }
  57. }
  58. }
  59. void MemoryTracker::logMalloc(thread_id_t thread_id, UInt64 eip, UInt64 address, UInt64 size)
  60. {
  61. ScopedLock sl(m_lock);
  62. ::RoutineTracerThread *tracer = Sim()->getThreadManager()->getThreadFromID(thread_id)->getRoutineTracer();
  63. const CallStack stack = dynamic_cast<MemoryTracker::RoutineTracerThread*>(tracer)->getCallsiteStack();
  64. AllocationSite *site = NULL;
  65. AllocationSites::iterator it = m_allocation_sites.find(stack);
  66. if (it != m_allocation_sites.end())
  67. site = it->second;
  68. else
  69. {
  70. site = new AllocationSite();
  71. m_allocation_sites[stack] = site;
  72. }
  73. // Store the first address of the first cache line that no longer belongs to the allocation
  74. UInt64 lower = address & ~63, upper = (address + size + 63) & ~63;
  75. //printf("memtracker: site %p(%lx) malloc %lx + %10lx (%lx .. %lx)\n", site, eip, address, size, lower, upper);
  76. auto previous = m_allocations.upper_bound(lower);
  77. if (previous != m_allocations.end() && lower >= previous->first - previous->second.size)
  78. {
  79. //printf("\t%p overwriting %p\n", site, previous->second.site);
  80. if (previous->first - previous->second.size < lower)
  81. {
  82. UInt64 start = previous->first - previous->second.size;
  83. m_allocations[lower] = Allocation(lower - start, previous->second.site);
  84. //printf("\tremain %p %lx .. %lx\n", previous->second.site, start, lower);
  85. }
  86. if (previous->first > upper)
  87. {
  88. m_allocations[previous->first] = Allocation(previous->first - upper, previous->second.site);
  89. //printf("\tremain %p %lx .. %lx\n", previous->second.site, upper, previous->first);
  90. }
  91. else
  92. {
  93. m_allocations.erase(previous);
  94. }
  95. }
  96. m_allocations[upper] = Allocation(upper - lower, site);
  97. #ifdef ASSERT_FIND_OWNER
  98. for(UInt64 addr = lower; addr < upper; addr += 64)
  99. m_allocations_slow[addr] = site;
  100. #endif
  101. m_allocation_sites[stack]->num_allocations++;
  102. m_allocation_sites[stack]->total_size += size;
  103. }
  104. void MemoryTracker::logFree(thread_id_t thread_id, UInt64 eip, UInt64 address)
  105. {
  106. ScopedLock sl(m_lock);
  107. //printf("memtracker: free %lx\n", address);
  108. }
  109. UInt64 MemoryTracker::ce_get_owner(core_id_t core_id, UInt64 address)
  110. {
  111. ScopedLock sl(m_lock);
  112. AllocationSite *owner = NULL;
  113. // upper_bound returns the first entry greater than address
  114. // Because the key in m_allocations is the first cache line that no longer falls into the range,
  115. // we will find the correct alloction *if* address falls within it
  116. auto upper = m_allocations.upper_bound(address);
  117. if (upper != m_allocations.end() && address >= upper->first - upper->second.size)
  118. owner = upper->second.site;
  119. #ifdef ASSERT_FIND_OWNER
  120. AllocationSite *owner_slow = (m_allocations_slow.count(address & ~63) == 0) ? NULL : m_allocations_slow[address & ~63];
  121. LOG_ASSERT_WARNING(owner == owner_slow, "ASSERT_FIND_OWNER: owners for %lx don't match (fast %p != slow %p)", address, owner, owner_slow);
  122. #endif
  123. return (UInt64)owner;
  124. }
  125. void MemoryTracker::ce_notify_access(UInt64 owner, Core::mem_op_t mem_op_type, HitWhere::where_t hit_where)
  126. {
  127. if (owner)
  128. {
  129. AllocationSite *site = (AllocationSite*)owner;
  130. if (mem_op_type == Core::WRITE)
  131. {
  132. site->total_stores++;
  133. site->hit_where_store[hit_where]++;
  134. }
  135. else
  136. {
  137. site->total_loads++;
  138. site->hit_where_load[hit_where]++;
  139. }
  140. }
  141. }
  142. void MemoryTracker::ce_notify_evict(bool on_roi_end, UInt64 owner, UInt64 evictor, CacheBlockInfo::BitsUsedType bits_used, UInt32 bits_total)
  143. {
  144. if (!on_roi_end && owner)
  145. {
  146. AllocationSite *site = (AllocationSite*)owner;
  147. AllocationSite *evictor_site = (AllocationSite*)evictor;
  148. if (site->evicted_by.count(evictor_site) == 0)
  149. site->evicted_by[evictor_site] = 0;
  150. site->evicted_by[evictor_site]++;
  151. }
  152. }
  153. MemoryTracker::RoutineTracer::RoutineTracer()
  154. {
  155. Sim()->setMemoryTracker(new MemoryTracker());
  156. }
  157. MemoryTracker::RoutineTracer::~RoutineTracer()
  158. {
  159. delete Sim()->getMemoryTracker();
  160. }
  161. void MemoryTracker::RoutineTracer::addRoutine(IntPtr eip, const char *name, const char *imgname, IntPtr offset, int column, int line, const char *filename)
  162. {
  163. ScopedLock sl(m_lock);
  164. if (m_routines.count(eip) == 0)
  165. {
  166. m_routines[eip] = new RoutineTracer::Routine(eip, name, imgname, offset, column, line, filename);
  167. }
  168. }
  169. bool MemoryTracker::RoutineTracer::hasRoutine(IntPtr eip)
  170. {
  171. ScopedLock sl(m_lock);
  172. return m_routines.count(eip) > 0;
  173. }
  174. void MemoryTracker::RoutineTracerThread::functionEnter(IntPtr eip, IntPtr callEip)
  175. {
  176. m_callsite_stack.push_back(callEip);
  177. }
  178. void MemoryTracker::RoutineTracerThread::functionExit(IntPtr eip)
  179. {
  180. m_callsite_stack.pop_back();
  181. }