1// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/trace_event/heap_profiler_type_name_deduplicator.h"
6
7#include <stddef.h>
8#include <stdlib.h>
9#include <string>
10#include <utility>
11
12#include "base/json/string_escape.h"
13#include "base/strings/stringprintf.h"
14#include "base/trace_event/trace_event_memory_overhead.h"
15
16namespace base {
17namespace trace_event {
18
19namespace {
20
21// Extract directory name if |type_name| was file name. Otherwise, return
22// |type_name|.
23StringPiece ExtractDirNameFromFileName(const char* type_name) {
24  StringPiece result(type_name);
25  size_t last_seperator = result.find_last_of("\\/");
26
27  // If |type_name| was a not a file path, the seperator will not be found, so
28  // the whole type name is returned.
29  if (last_seperator == StringPiece::npos)
30    return result;
31
32  // Remove the file name from the path.
33  result.remove_suffix(result.length() - last_seperator);
34
35  // Remove the parent directory references.
36  const char kParentDirectory[] = "..";
37  const size_t kParentDirectoryLength = 3; // '../' or '..\'.
38  while (result.starts_with(kParentDirectory)) {
39    result.remove_prefix(kParentDirectoryLength);
40  }
41  return result;
42}
43
44}  // namespace
45
46TypeNameDeduplicator::TypeNameDeduplicator() {
47  // A null pointer has type ID 0 ("unknown type");
48  type_ids_.insert(std::make_pair(nullptr, 0));
49}
50
51TypeNameDeduplicator::~TypeNameDeduplicator() {}
52
53int TypeNameDeduplicator::Insert(const char* type_name) {
54  auto result = type_ids_.insert(std::make_pair(type_name, 0));
55  auto& elem = result.first;
56  bool did_not_exist_before = result.second;
57
58  if (did_not_exist_before) {
59    // The type IDs are assigned sequentially and they are zero-based, so
60    // |size() - 1| is the ID of the new element.
61    elem->second = static_cast<int>(type_ids_.size() - 1);
62  }
63
64  return elem->second;
65}
66
67void TypeNameDeduplicator::AppendAsTraceFormat(std::string* out) const {
68  out->append("{");  // Begin the type names dictionary.
69
70  auto it = type_ids_.begin();
71  std::string buffer;
72
73  // Write the first entry manually; the null pointer must not be dereferenced.
74  // (The first entry is the null pointer because a |std::map| is ordered.)
75  it++;
76  out->append("\"0\":\"[unknown]\"");
77
78  for (; it != type_ids_.end(); it++) {
79    // Type IDs in the trace are strings, write them as stringified keys of
80    // a dictionary.
81    SStringPrintf(&buffer, ",\"%d\":", it->second);
82
83    // TODO(ssid): crbug.com/594803 the type name is misused for file name in
84    // some cases.
85    StringPiece type_info = ExtractDirNameFromFileName(it->first);
86
87    // |EscapeJSONString| appends, it does not overwrite |buffer|.
88    bool put_in_quotes = true;
89    EscapeJSONString(type_info, put_in_quotes, &buffer);
90    out->append(buffer);
91  }
92
93  out->append("}");  // End the type names dictionary.
94}
95
96void TypeNameDeduplicator::EstimateTraceMemoryOverhead(
97    TraceEventMemoryOverhead* overhead) {
98  // The size here is only an estimate; it fails to take into account the size
99  // of the tree nodes for the map, but as an estimate this should be fine.
100  size_t map_size = type_ids_.size() * sizeof(std::pair<const char*, int>);
101
102  overhead->Add("TypeNameDeduplicator",
103                sizeof(TypeNameDeduplicator) + map_size);
104}
105
106}  // namespace trace_event
107}  // namespace base
108