Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2009 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 "net/base/load_log_util.h"
      6 
      7 #include "base/format_macros.h"
      8 #include "base/string_util.h"
      9 #include "net/base/net_errors.h"
     10 
     11 namespace net {
     12 namespace {
     13 
     14 class FormatHelper {
     15  public:
     16   std::string ToString(const LoadLog* log) {
     17     entries_.clear();
     18 
     19     // Pass 1: Match the start/end of indentation blocks. Fills |entries_|
     20     // with the results.
     21     PopulateEntries(log);
     22 
     23     // Pass 2: Figure out the maximum width of each column. This allows us
     24     // to right-justify text within each column.
     25     size_t max_time_width, max_indentation, max_type_width, max_dt_width;
     26     GetMaxExtent(
     27         &max_time_width, &max_indentation, &max_type_width, &max_dt_width);
     28 
     29     // Pass 3: Assemble the string.
     30     std::string result;
     31 
     32     const int kSpacesPerIndentation = 2;
     33 
     34     for (size_t i = 0; i < entries_.size(); ++i) {
     35       if (log->num_entries_truncated() > 0 && i + 1 == entries_.size()) {
     36         StringAppendF(&result, " ... Truncated %" PRIuS " entries ...\n",
     37                       log->num_entries_truncated());
     38       }
     39 
     40       if (entries_[i].block_index != -1 &&
     41           static_cast<size_t>(entries_[i].block_index + 1) == i) {
     42         // If there were no entries in between the START/END block then don't
     43         // bother printing a line for END (it just adds noise, and we already
     44         // show the time delta besides START anyway).
     45         continue;
     46       }
     47 
     48       int indentation_spaces = entries_[i].indentation * kSpacesPerIndentation;
     49       std::string entry_str = GetEntryString(i);
     50 
     51       StringAppendF(&result, "t=%s: %s%s",
     52                     PadStringLeft(GetTimeString(i), max_time_width).c_str(),
     53                     PadStringLeft("", indentation_spaces).c_str(),
     54                     entry_str.c_str());
     55 
     56       if (entries_[i].IsBeginEvent()) {
     57         // Summarize how long this block lasted.
     58         int padding = ((max_indentation - entries_[i].indentation) *
     59             kSpacesPerIndentation) + (max_type_width - entry_str.size());
     60         StringAppendF(&result, "%s [dt=%s]",
     61                       PadStringLeft("", padding).c_str(),
     62                       PadStringLeft(GetBlockDtString(i), max_dt_width).c_str());
     63       }
     64 
     65       if (i + 1 != entries_.size())
     66         result += "\n";
     67     }
     68 
     69     return result;
     70   }
     71 
     72  private:
     73   struct Entry {
     74     explicit Entry(const LoadLog::Entry* log_entry)
     75         : log_entry(log_entry), indentation(0), block_index(-1) {}
     76 
     77     bool IsBeginEvent() const {
     78       return log_entry->type == LoadLog::Entry::TYPE_EVENT &&
     79              log_entry->event.phase == LoadLog::PHASE_BEGIN;
     80     }
     81 
     82     bool IsEndEvent() const {
     83       return log_entry->type == LoadLog::Entry::TYPE_EVENT &&
     84              log_entry->event.phase == LoadLog::PHASE_END;
     85     }
     86 
     87     const LoadLog::Entry* log_entry;
     88     size_t indentation;
     89     int block_index;  // The index of the matching start / end of block.
     90   };
     91 
     92   void PopulateEntries(const LoadLog* log) {
     93     int current_indentation = 0;
     94 
     95     for (size_t i = 0; i < log->entries().size(); ++i) {
     96       Entry entry(&log->entries()[i]);
     97 
     98       entry.indentation = current_indentation;
     99 
    100       if (entry.IsBeginEvent()) {
    101         // Indent everything contained in this block.
    102         current_indentation++;
    103       }
    104 
    105       if (entry.IsEndEvent()) {
    106         int start_index = FindStartOfBlockIndex(entry);
    107         if (start_index != -1) {
    108           // Point the start / end of block at each other.
    109           entry.block_index = start_index;
    110           entries_[start_index].block_index = i;
    111 
    112           // Restore the indentation prior to the block.
    113           // (Could be more than 1 level if close of blocks are missing).
    114           current_indentation = entries_[start_index].indentation;
    115           entry.indentation = current_indentation;
    116         }
    117       }
    118 
    119       entries_.push_back(entry);
    120     }
    121   }
    122 
    123   int FindStartOfBlockIndex(const Entry& entry) {
    124     DCHECK(entry.IsEndEvent());
    125 
    126     // Find the matching  start of block by scanning backwards.
    127     for (int i = entries_.size() - 1; i >= 0; --i) {
    128       if (entries_[i].IsBeginEvent() &&
    129           entries_[i].log_entry->event.type == entry.log_entry->event.type) {
    130         return i;
    131       }
    132     }
    133     return -1;  // Start not found.
    134   }
    135 
    136   void GetMaxExtent(size_t* max_time_width,
    137                     size_t* max_indentation,
    138                     size_t* max_type_width,
    139                     size_t* max_dt_width) {
    140     *max_time_width = *max_indentation = *max_type_width = *max_dt_width = 0;
    141     for (size_t i = 0; i < entries_.size(); ++i) {
    142       *max_time_width = std::max(*max_time_width, GetTimeString(i).size());
    143       if (entries_[i].log_entry->type == LoadLog::Entry::TYPE_EVENT)
    144         *max_type_width = std::max(*max_type_width, GetEntryString(i).size());
    145       *max_indentation = std::max(*max_indentation, entries_[i].indentation);
    146 
    147       if (entries_[i].IsBeginEvent())
    148         *max_dt_width = std::max(*max_dt_width, GetBlockDtString(i).size());
    149     }
    150   }
    151 
    152   std::string GetBlockDtString(size_t start_index) {
    153     int end_index = entries_[start_index].block_index;
    154     if (end_index == -1) {
    155       // Block is not closed, implicitly close it at EOF.
    156       end_index = entries_.size() - 1;
    157     }
    158     int64 dt_ms = (entries_[end_index].log_entry->time -
    159                    entries_[start_index].log_entry->time).InMilliseconds();
    160 
    161     return Int64ToString(dt_ms);
    162   }
    163 
    164   std::string GetTimeString(size_t index) {
    165     int64 t_ms = (entries_[index].log_entry->time -
    166                   base::TimeTicks()).InMilliseconds();
    167     return Int64ToString(t_ms);
    168   }
    169 
    170   std::string GetEntryString(size_t index) {
    171     const LoadLog::Entry* entry = entries_[index].log_entry;
    172 
    173     std::string entry_str;
    174     LoadLog::EventPhase phase = LoadLog::PHASE_NONE;
    175     switch (entry->type) {
    176       case LoadLog::Entry::TYPE_EVENT:
    177         entry_str = LoadLog::EventTypeToString(entry->event.type);
    178         phase = entry->event.phase;
    179 
    180         if (phase == LoadLog::PHASE_BEGIN &&
    181             index + 1 < entries_.size() &&
    182             static_cast<size_t>(entries_[index + 1].block_index) == index) {
    183           // If this starts an empty block, we will pretend it is a PHASE_NONE
    184           // so we don't print the "+" prefix.
    185           phase = LoadLog::PHASE_NONE;
    186         }
    187         break;
    188       case LoadLog::Entry::TYPE_ERROR_CODE:
    189         entry_str = StringPrintf("error code: %d (%s)",
    190                                  entry->error_code,
    191                                  ErrorToString(entry->error_code));
    192         break;
    193       case LoadLog::Entry::TYPE_STRING:
    194         entry_str = StringPrintf("\"%s\"", entry->string.c_str());
    195         break;
    196       case LoadLog::Entry::TYPE_STRING_LITERAL:
    197         entry_str = StringPrintf("\"%s\"", entry->literal);
    198         break;
    199       default:
    200         NOTREACHED();
    201     }
    202 
    203     switch (phase) {
    204       case LoadLog::PHASE_BEGIN:
    205         return std::string("+") + entry_str;
    206       case LoadLog::PHASE_END:
    207         return std::string("-") + entry_str;
    208       case LoadLog::PHASE_NONE:
    209         return std::string(" ") + entry_str;
    210       default:
    211         NOTREACHED();
    212         return std::string();
    213     }
    214   }
    215 
    216   static std::string PadStringLeft(const std::string& str, size_t width) {
    217     DCHECK_LE(str.size(), width);
    218     std::string padding;
    219     padding.resize(width - str.size(), ' ');
    220     return padding + str;
    221   }
    222 
    223   std::vector<Entry> entries_;
    224 };
    225 
    226 }  // namespace
    227 
    228 // static
    229 std::string LoadLogUtil::PrettyPrintAsEventTree(const LoadLog* log) {
    230   FormatHelper helper;
    231   return helper.ToString(log);
    232 }
    233 
    234 }  // namespace net
    235