text: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  
  #include "util/memusage-path-handlers.h"
  
  #include <gperftools/malloc_extension.h>
  
  #include "runtime/mem-tracker.h"
  #include "util/common-metrics.h"
  #include "util/mem-info.h"
  #include "util/pretty-printer.h"
  
  #include "common/names.h"
  
  using namespace impala;
  using namespace rapidjson;
  
  /// A MemTracker tracks memory consumption and limits which will be displayed. This method
  /// adds the mem_limit and the current memory consumption to the rapidjson document. Also,
  /// it dumps all the additional mem trackers to the rapidjson document.
  void AddMemTracker(MemTracker* mem_tracker,
      const Webserver::WebRequest& req, Document* document) {
    DCHECK(mem_tracker != NULL);
    Value mem_limit(PrettyPrinter::Print(mem_tracker->limit(), TUnit::BYTES).c_str(),
        document->GetAllocator());
    document->AddMember("mem_limit", mem_limit, document->GetAllocator());
    Value consumption(
        PrettyPrinter::Print(mem_tracker->consumption(), TUnit::BYTES).c_str(),
        document->GetAllocator());
    document->AddMember("consumption", consumption, document->GetAllocator());
  
    // Dump all mem trackers.
    Value detailed(mem_tracker->LogUsage(MemTracker::UNLIMITED_DEPTH).c_str(),
        document->GetAllocator());
    document->AddMember("detailed", detailed, document->GetAllocator());
  }
  
  /// Adds the following malloc data structures to the rapidjson document.
  /// - Total inuse memory by application.
  /// - Free memory(thread, central and page heap),
  /// - Freelist of central cache, each class.
  /// - Page heap freelist.
  void AddTCmallocOverview(const Webserver::WebRequest& req, Document* document) {
    stringstream ss;
  #if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
    ss << "Memory tracking is not available with address or thread sanitizer builds.";
  #else
    char buf[2048];
    MallocExtension::instance()->GetStats(buf, 2048);
    ss << string(buf);
  #endif
  
    Value overview(ss.str().c_str(), document->GetAllocator());
    document->AddMember("overview", overview, document->GetAllocator());
  }
  
  /// Adds the system memory configuration to the rapidjson document.
  void AddSystemInfo(const Webserver::WebRequest& req, Document* document) {
    Value systeminfo(MemInfo::DebugString().c_str(), document->GetAllocator());
    document->AddMember("systeminfo", systeminfo, document->GetAllocator());
  }
  
  /// A MetricGroup is a container for a set of metric. This method extracts and adds the
  /// initialized JVM, buffer-pool and aggregated memory metrics to the rapidjson document
  /// from the given MetricGroup.
  void AddMetricGroup(MetricGroup* metric_group,
      const Webserver::WebRequest& req, Document* document) {
    if (metric_group != nullptr) {
      MetricGroup* aggregate_group = metric_group->FindChildGroup("memory");
      if (aggregate_group != nullptr) {
        Value json_metrics(kObjectType);
        aggregate_group->ToJson(false, document, &json_metrics);
        document->AddMember(
            "aggregate_metrics", json_metrics["metrics"], document->GetAllocator());
      }
      MetricGroup* jvm_group = metric_group->FindChildGroup("jvm");
      if (jvm_group != nullptr) {
        Value jvm(kObjectType);
        jvm_group->ToJson(false, document, &jvm);
        Value heap(kArrayType);
        Value non_heap(kArrayType);
        Value total(kArrayType);
        for (SizeType i = 0; i < jvm["metrics"].Size(); ++i) {
          if (strstr(jvm["metrics"][i]["name"].GetString(), "total") != nullptr) {
            total.PushBack(jvm["metrics"][i], document->GetAllocator());
          } else if (strstr(jvm["metrics"][i]["name"].GetString(), "non-heap") != nullptr) {
            non_heap.PushBack(jvm["metrics"][i], document->GetAllocator());
          } else if (strstr(jvm["metrics"][i]["name"].GetString(), "heap") != nullptr) {
            heap.PushBack(jvm["metrics"][i], document->GetAllocator());
          }
        }
        document->AddMember("jvm_total", total, document->GetAllocator());
        document->AddMember("jvm_heap", heap, document->GetAllocator());
        document->AddMember("jvm_non_heap", non_heap, document->GetAllocator());
      }
      MetricGroup* buffer_pool_group = metric_group->FindChildGroup("buffer-pool");
      if (buffer_pool_group != nullptr) {
        Value json_metrics(kObjectType);
        buffer_pool_group->ToJson(false, document, &json_metrics);
        document->AddMember(
            "buffer_pool", json_metrics["metrics"], document->GetAllocator());
      }
    }
  }
  
  void impala::AddMemUsageCallbacks(Webserver* webserver, MemTracker* mem_tracker,
      MetricGroup* metric_group) {
    if (mem_tracker != nullptr) {
      auto callback = [mem_tracker, metric_group]
          (const Webserver::WebRequest& req, Document* doc) {
        AddMemTracker(mem_tracker, req, doc);
        AddTCmallocOverview(req, doc);
        AddSystemInfo(req, doc);
        AddMetricGroup(metric_group, req, doc);
      };
      webserver->RegisterUrlCallback("/memz", "memz.tmpl", callback, true);
    } else {
      auto callback = [metric_group]
          (const Webserver::WebRequest& req, Document* doc) {
        AddTCmallocOverview(req, doc);
        AddSystemInfo(req, doc);
        AddMetricGroup(metric_group, req, doc);
      };
      webserver->RegisterUrlCallback("/memz", "memz.tmpl", callback, true);
    }
  }
  