static void InitTraceLog(void)
{
  if (gInitialized) return;
  gInitialized = true;

  bool defined;
  defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
  if (!defined)
    gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
  if (defined || gLogLeaksOnly) {
    RecreateBloatView();
    if (!gBloatView) {
      NS_WARNING("out of memory");
      gBloatLog = nullptr;
      gLogLeaksOnly = false;
    }
  }

  (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);

  (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);

  defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
  if (defined) {
    gLogToLeaky = true;
    PRFuncPtr p = nullptr, q = nullptr;
#ifdef HAVE_DLOPEN
    {
      PRLibrary *lib = nullptr;
      p = PR_FindFunctionSymbolAndLibrary("__log_addref", &lib);
      if (lib) {
        PR_UnloadLibrary(lib);
        lib = nullptr;
      }
      q = PR_FindFunctionSymbolAndLibrary("__log_release", &lib);
      if (lib) {
        PR_UnloadLibrary(lib);
      }
    }
#endif
    if (p && q) {
      leakyLogAddRef = (void (*)(void*,int,int)) p;
      leakyLogRelease = (void (*)(void*,int,int)) q;
    }
    else {
      gLogToLeaky = false;
      fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
      fflush(stdout);
    }
  }

  const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");

#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
  if (classes) {
    (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
  } else {
    if (getenv("XPCOM_MEM_COMPTR_LOG")) {
      fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
    }
  }
#else
  const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
  if (comptr_log) {
    fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
  }
#endif

  if (classes) {
    // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
    // as a list of class names to track
    gTypesToLog = PL_NewHashTable(256,
                                  PL_HashString,
                                  PL_CompareStrings,
                                  PL_CompareValues,
                                  &typesToLogHashAllocOps, NULL);
    if (!gTypesToLog) {
      NS_WARNING("out of memory");
      fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
    }
    else {
      fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
      const char* cp = classes;
      for (;;) {
        char* cm = (char*) strchr(cp, ',');
        if (cm) {
          *cm = '\0';
        }
        PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
        fprintf(stdout, "%s ", cp);
        if (!cm) break;
        *cm = ',';
        cp = cm + 1;
      }
      fprintf(stdout, "\n");
    }

    gSerialNumbers = PL_NewHashTable(256,
                                     HashNumber,
                                     PL_CompareValues,
                                     PL_CompareValues,
                                     &serialNumberHashAllocOps, NULL);


  }

  const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
  if (objects) {
    gObjectsToLog = PL_NewHashTable(256,
                                    HashNumber,
                                    PL_CompareValues,
                                    PL_CompareValues,
                                    NULL, NULL);

    if (!gObjectsToLog) {
      NS_WARNING("out of memory");
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
    }
    else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) {
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
    }
    else {
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
      const char* cp = objects;
      for (;;) {
        char* cm = (char*) strchr(cp, ',');
        if (cm) {
          *cm = '\0';
        }
        int32_t top = 0;
        int32_t bottom = 0;
        while (*cp) {
          if (*cp == '-') {
            bottom = top;
            top = 0;
            ++cp;
          }
          top *= 10;
          top += *cp - '0';
          ++cp;
        }
        if (!bottom) {
          bottom = top;
        }
        for(int32_t serialno = bottom; serialno <= top; serialno++) {
          PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
          fprintf(stdout, "%d ", serialno);
        }
        if (!cm) break;
        *cm = ',';
        cp = cm + 1;
      }
      fprintf(stdout, "\n");
    }
  }


  if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
    gLogging = true;
  }

  gTraceLock = PR_NewLock();
}
Beispiel #2
0
int main(int argc, char **argv)
{
    if (argc != 2) {
        fprintf(stderr,
                "Expected usage:  %s <sd-leak-file>\n"
                "  sd-leak-file: Output of --shutdown-leaks=<file> option.\n",
                argv[0]);
        return 1;
    }

    NS_InitXPCOM2(NULL, NULL, NULL);

    ADLog log;
    if (!log.Read(argv[1])) {
        fprintf(stderr,
                "%s: Error reading input file %s.\n", argv[0], argv[1]);
    }

    const size_t count = log.count();

    PLHashTable *memory_map =
        PL_NewHashTable(count * 8, hash_pointer, PL_CompareValues,
                        PL_CompareValues, 0, 0);
    if (!memory_map) {
        fprintf(stderr, "%s: Out of memory.\n", argv[0]);
        return 1;
    }

    // Create one |AllocationNode| object for each log entry, and create
    // entries in the hashtable pointing to it for each byte it occupies.
    AllocationNode *nodes = new AllocationNode[count];
    if (!nodes) {
        fprintf(stderr, "%s: Out of memory.\n", argv[0]);
        return 1;
    }

    {
        AllocationNode *cur_node = nodes;
        for (ADLog::const_iterator entry = log.begin(), entry_end = log.end();
             entry != entry_end; ++entry, ++cur_node) {
            const ADLog::Entry *e = cur_node->entry = *entry;
            cur_node->reached = PR_FALSE;

            for (ADLog::Pointer p = e->address,
                            p_end = e->address + e->datasize;
                 p != p_end; ++p) {
                PLHashEntry *e = PL_HashTableAdd(memory_map, p, cur_node);
                if (!e) {
                    fprintf(stderr, "%s: Out of memory.\n", argv[0]);
                    return 1;
                }
            }
        }
    }

    // Construct graph based on pointers.
    for (AllocationNode *node = nodes, *node_end = nodes + count;
         node != node_end; ++node) {
        const ADLog::Entry *e = node->entry;
        for (const char *d = e->data, *d_end = e->data + e->datasize -
                                         e->datasize % sizeof(ADLog::Pointer);
             d != d_end; d += sizeof(ADLog::Pointer)) {
            AllocationNode *target = (AllocationNode*)
                PL_HashTableLookup(memory_map, *(void**)d);
            if (target) {
                target->pointers_from.AppendElement(node);
                node->pointers_to.AppendElement(target);
            }
        }
    }

    // Do a depth-first search on the graph (i.e., by following
    // |pointers_to|) and assign the post-order index to |index|.
    {
        PRUint32 dfs_index = 0;
        nsVoidArray stack;

        for (AllocationNode *n = nodes, *n_end = nodes+count; n != n_end; ++n) {
            if (n->reached) {
                continue;
            }
            stack.AppendElement(n);

            do {
                PRUint32 pos = stack.Count() - 1;
                AllocationNode *n =
                    static_cast<AllocationNode*>(stack[pos]);
                if (n->reached) {
                    n->index = dfs_index++;
                    stack.RemoveElementAt(pos);
                } else {
                    n->reached = PR_TRUE;

                    // When doing post-order processing, we have to be
                    // careful not to put reached nodes into the stack.
                    nsVoidArray &pt = n->pointers_to;
                    for (PRInt32 i = pt.Count() - 1; i >= 0; --i) {
                        if (!static_cast<AllocationNode*>(pt[i])->reached) {
                            stack.AppendElement(pt[i]);
                        }
                    }
                }
            } while (stack.Count() > 0);
        }
    }

    // Sort the nodes by their DFS index, in reverse, so that the first
    // node is guaranteed to be in a root SCC.
    AllocationNode **sorted_nodes = new AllocationNode*[count];
    if (!sorted_nodes) {
        fprintf(stderr, "%s: Out of memory.\n", argv[0]);
        return 1;
    }

    {
        for (size_t i = 0; i < count; ++i) {
            sorted_nodes[i] = nodes + i;
        }
        NS_QuickSort(sorted_nodes, count, sizeof(AllocationNode*),
                     sort_by_reverse_index, 0);
    }

    // Put the nodes into their strongly-connected components.
    PRUint32 num_sccs = 0;
    {
        for (size_t i = 0; i < count; ++i) {
            nodes[i].reached = PR_FALSE;
        }
        nsVoidArray stack;
        for (AllocationNode **sn = sorted_nodes,
                        **sn_end = sorted_nodes + count; sn != sn_end; ++sn) {
            if ((*sn)->reached) {
                continue;
            }

            // We found a new strongly connected index.
            stack.AppendElement(*sn);
            do {
                PRUint32 pos = stack.Count() - 1;
                AllocationNode *n =
                    static_cast<AllocationNode*>(stack[pos]);
                stack.RemoveElementAt(pos);

                if (!n->reached) {
                    n->reached = PR_TRUE;
                    n->index = num_sccs;
                    stack.AppendElements(n->pointers_from);
                }
            } while (stack.Count() > 0);
            ++num_sccs;
        }
    }

    // Identify which nodes are leak roots by using DFS, and watching
    // for component transitions.
    PRUint32 num_root_nodes = count;
    {
        for (size_t i = 0; i < count; ++i) {
            nodes[i].is_root = PR_TRUE;
        }

        nsVoidArray stack;
        for (AllocationNode *n = nodes, *n_end = nodes+count; n != n_end; ++n) {
            if (!n->is_root) {
                continue;
            }

            // Loop through pointers_to, and add any that are in a
            // different SCC to stack:
            for (int i = n->pointers_to.Count() - 1; i >= 0; --i) {
                AllocationNode *target =
                    static_cast<AllocationNode*>(n->pointers_to[i]);
                if (n->index != target->index) {
                    stack.AppendElement(target);
                }
            }

            while (stack.Count() > 0) {
                PRUint32 pos = stack.Count() - 1;
                AllocationNode *n =
                    static_cast<AllocationNode*>(stack[pos]);
                stack.RemoveElementAt(pos);

                if (n->is_root) {
                    n->is_root = PR_FALSE;
                    --num_root_nodes;
                    stack.AppendElements(n->pointers_to);
                }
            }
        }
    }

    // Sort the nodes by their SCC index.
    NS_QuickSort(sorted_nodes, count, sizeof(AllocationNode*),
                 sort_by_index, 0);

    // Print output.
    {
        printf("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
               "<html>\n"
               "<head>\n"
               "<title>Leak analysis</title>\n"
               "<style type=\"text/css\">\n"
               "  .root { background: white; color: black; }\n"
               "  .nonroot { background: #ccc; color: black; }\n"
               "</style>\n"
               "</head>\n");
        printf("<body>\n\n"
               "<p>Generated %d entries (%d in root SCCs) and %d SCCs.</p>\n\n",
               count, num_root_nodes, num_sccs);

        for (size_t i = 0; i < count; ++i) {
            nodes[i].reached = PR_FALSE;
        }

        // Loop over the sorted nodes twice, first printing the roots
        // and then the non-roots.
        for (PRBool root_type = PR_TRUE;
             root_type == PR_TRUE || root_type == PR_FALSE; --root_type) {
            if (root_type) {
                printf("\n\n"
                       "<div class=\"root\">\n"
                       "<h1 id=\"root\">Root components</h1>\n");
            } else {
                printf("\n\n"
                       "<div class=\"nonroot\">\n"
                       "<h1 id=\"nonroot\">Non-root components</h1>\n");
            }
            PRUint32 component = (PRUint32)-1;
            PRBool one_object_component;
            for (const AllocationNode *const* sn = sorted_nodes,
                                  *const* sn_end = sorted_nodes + count;
                 sn != sn_end; ++sn) {
                const AllocationNode *n = *sn;
                if (n->is_root != root_type)
                    continue;
                const ADLog::Entry *e = n->entry;

                if (n->index != component) {
                    component = n->index;
                    one_object_component =
                        sn + 1 == sn_end || (*(sn+1))->index != component;
                    if (!one_object_component)
                        printf("\n\n<h2 id=\"c%d\">Component %d</h2>\n",
                               component, component);
                }

                if (one_object_component) {
                    printf("\n\n<div id=\"c%d\">\n", component);
                    printf("<h2 id=\"o%d\">Object %d "
                           "(single-object component %d)</h2>\n",
                           n-nodes, n-nodes, component);
                } else {
                    printf("\n\n<h3 id=\"o%d\">Object %d</h3>\n",
                           n-nodes, n-nodes);
                }
                printf("<pre>\n");
                printf("%p &lt;%s&gt; (%d)\n",
                       e->address, e->type, e->datasize);
                for (size_t d = 0; d < e->datasize;
                     d += sizeof(ADLog::Pointer)) {
                    AllocationNode *target = (AllocationNode*)
                        PL_HashTableLookup(memory_map, *(void**)(e->data + d));
                    if (target) {
                        printf("        <a href=\"#o%d\">0x%08X</a> &lt;%s&gt;",
                               target - nodes,
                               *(unsigned int*)(e->data + d),
                               target->entry->type);
                        if (target->index != n->index) {
                            printf(", component %d", target->index);
                        }
                        printf("\n");
                    } else {
                        printf("        0x%08X\n",
                               *(unsigned int*)(e->data + d));
                    }
                }

                if (n->pointers_from.Count()) {
                    printf("\nPointers from:\n");
                    for (PRUint32 i = 0, i_end = n->pointers_from.Count();
                         i != i_end; ++i) {
                        AllocationNode *t = static_cast<AllocationNode*>
                                                       (n->pointers_from[i]);
                        const ADLog::Entry *te = t->entry;
                        printf("    <a href=\"#o%d\">%s</a> (Object %d, ",
                               t - nodes, te->type, t - nodes);
                        if (t->index != n->index) {
                            printf("component %d, ", t->index);
                        }
                        if (t == n) {
                            printf("self)\n");
                        } else {
                            printf("%p)\n", te->address);
                        }
                    }
                }

                print_escaped(stdout, e->allocation_stack);

                printf("</pre>\n");
                if (one_object_component) {
                    printf("</div>\n");
                }
            }
            printf("</div>\n");
        }
        printf("</body>\n"
               "</html>\n");
    }

    delete [] sorted_nodes;
    delete [] nodes;

    NS_ShutdownXPCOM(NULL);

    return 0;
}
Beispiel #3
0
static void
InitTraceLog()
{
  if (gInitialized) {
    return;
  }
  gInitialized = true;

  bool defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
  if (!defined) {
    gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
  }
  if (defined || gLogLeaksOnly) {
    RecreateBloatView();
    if (!gBloatView) {
      NS_WARNING("out of memory");
      maybeUnregisterAndCloseFile(gBloatLog);
      gLogLeaksOnly = false;
    }
  }

  InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);

  InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);

  const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");

#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
  if (classes) {
    InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
  } else {
    if (getenv("XPCOM_MEM_COMPTR_LOG")) {
      fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
    }
  }
#else
  const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
  if (comptr_log) {
    fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
  }
#endif

  if (classes) {
    // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
    // as a list of class names to track
    gTypesToLog = PL_NewHashTable(256,
                                  PL_HashString,
                                  PL_CompareStrings,
                                  PL_CompareValues,
                                  &typesToLogHashAllocOps, nullptr);
    if (!gTypesToLog) {
      NS_WARNING("out of memory");
      fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
    } else {
      fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
      const char* cp = classes;
      for (;;) {
        char* cm = (char*)strchr(cp, ',');
        if (cm) {
          *cm = '\0';
        }
        PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1);
        fprintf(stdout, "%s ", cp);
        if (!cm) {
          break;
        }
        *cm = ',';
        cp = cm + 1;
      }
      fprintf(stdout, "\n");
    }

    gSerialNumbers = PL_NewHashTable(256,
                                     HashNumber,
                                     PL_CompareValues,
                                     PL_CompareValues,
                                     &serialNumberHashAllocOps, nullptr);


  }

  const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
  if (objects) {
    gObjectsToLog = PL_NewHashTable(256,
                                    HashNumber,
                                    PL_CompareValues,
                                    PL_CompareValues,
                                    nullptr, nullptr);

    if (!gObjectsToLog) {
      NS_WARNING("out of memory");
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
    } else if (!(gRefcntsLog || gAllocLog || gCOMPtrLog)) {
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
    } else {
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
      const char* cp = objects;
      for (;;) {
        char* cm = (char*)strchr(cp, ',');
        if (cm) {
          *cm = '\0';
        }
        intptr_t top = 0;
        intptr_t bottom = 0;
        while (*cp) {
          if (*cp == '-') {
            bottom = top;
            top = 0;
            ++cp;
          }
          top *= 10;
          top += *cp - '0';
          ++cp;
        }
        if (!bottom) {
          bottom = top;
        }
        for (intptr_t serialno = bottom; serialno <= top; serialno++) {
          PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
          fprintf(stdout, "%" PRIdPTR " ", serialno);
        }
        if (!cm) {
          break;
        }
        *cm = ',';
        cp = cm + 1;
      }
      fprintf(stdout, "\n");
    }
  }


  if (gBloatLog) {
    gLogging = OnlyBloatLogging;
  }

  if (gRefcntsLog || gAllocLog || gCOMPtrLog) {
    gLogging = FullLogging;
  }
}