<?php
/**
 * Summary of heap
 */

require_once "WEB-INF/php/inc.php";

if (! admin_init()) {
  return;
}

echo "<h1>JVM Memory"; print_help("Memory"); echo "</h1>";

echo "<div id='memory-tabs'>";
echo "<ul>";
echo "<li><a href='#graphs'>Graphs</a></li>\n";
echo "<li><a href='#dump'>Heap Dump</a></li>\n";
echo "</ul>";

echo "<div id='graphs'>\n";

$stat_service = $g_mbean_server->lookup("resin:type=StatService");

$si = sprintf("%02d", $g_server->Index);

if ($stat_service) {
  echo "<h2>Memory Free</h2>\n";

  $end = time();
  $start = $end - 6 * 3600;
  stat_graph("memory", 600, 200, $start, $end,
             array("$si|JVM|Memory|Heap Memory Free",
                   "$si|JVM|Memory|Tenured Memory Free",
                   "$si|JVM|Memory|PermGen Memory Free"));

  echo "<h2>GC Time</h2>\n";

  $end = time();
  $start = $end - 6 * 3600;
  stat_graph("gc", 600, 200, $start, $end,
             array("$si|JVM|Memory|GC Time|PS MarkSweep",
                   "$si|JVM|Memory|GC Time|PS Scavenge"));
}

$memory = $g_mbean_server->lookup("resin:type=Memory");

echo "<h2>Memory Pools</h2>\n";
$row = 0;
echo "<table class='data'>\n"

echo "<tr>";
echo "<th>pool</th>\n";
echo "<th>% free</th>\n";
echo "<th>free"; print_help("Memory#free"); echo "</th>\n";
echo "<th>used"; print_help("Memory#used"); echo "</th>\n";
echo "<th>max"; print_help("Memory#max"); echo "</th>\n";
echo "<th>committed</th>\n";
echo "</tr>";

echo "<tr class='" . row_style($row++) . "'>";
echo "<td>CodeCache"; print_help("Memory#CodeCache"); echo "</td>";
echo "<td align='right'>" . sprintf("%.2f%%", 100 * $memory->CodeCacheFree / $memory->CodeCacheMax) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->CodeCacheFree / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->CodeCacheUsed / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->CodeCacheMax / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->CodeCacheCommitted / 1e6) . "</td>";
echo "</tr>\n";

echo "<tr class='" . row_style($row++) . "'>";
echo "<td>Eden"; print_help("Memory#Eden"); echo "</td>\n";
echo "<td align='right'>" . sprintf("%.2f%%", 100 * $memory->EdenFree / $memory->EdenMax) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->EdenFree / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->EdenUsed / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->EdenMax / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->EdenCommitted / 1e6) . "</td>";
echo "</tr>\n";

echo "<tr class='" . row_style($row++) . "'>";
echo "<td>PermGen"; print_help("Memory#PermGen"); echo "</td>";
echo "<td align='right'>" . sprintf("%.2f%%", 100 * $memory->PermGenFree / $memory->PermGenMax) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->PermGenFree / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->PermGenUsed / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->PermGenMax / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->PermGenCommitted / 1e6) . "</td>";
echo "</tr>\n";

echo "<tr class='" . row_style($row++) . "'>";
echo "<td>Survivor"; print_help("Memory#Survivor"); echo "</td>";
echo "<td align='right'>" . sprintf("%.2f%%", 100 * $memory->SurvivorFree / $memory->SurvivorMax) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->SurvivorFree / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->SurvivorUsed / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->SurvivorMax / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->SurvivorCommitted / 1e6) . "</td>";
echo "</tr>\n";

echo "<tr class='" . row_style($row++) . "'>";
echo "<td>Tenured"; print_help("Memory#Tenured"); echo "</td>";
echo "<td align='right'>" . sprintf("%.2f%%", 100 * $memory->TenuredFree / $memory->TenuredMax) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->TenuredFree / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->TenuredUsed / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->TenuredMax / 1e6) . "</td>";
echo "<td align='right'>" . sprintf("%.2fM", $memory->TenuredCommitted / 1e6) . "</td>";
echo "</tr>\n";

echo "</table>";

echo "</div>\n";
echo "<div id='dump'>\n";

echo "<h2>Heap Dump</h2>";

$heap_dump_cl = java_class("com.caucho.profile.HeapDump");

if ($heap_dump_cl->isAvailable()) {
  $heap = @$heap_dump_cl->create();
}

$is_heap_available_heap = false;

$heap_file = "/tmp/java.hprof";

if ($heap) {
  echo "<form action='?q=memory' method='post'>";
  echo "Resin Heap Dump: <input type='submit' name='action' value='dump heap'><br>";
  echo "JVM Heap Dump: <input type='submit' name='action' value='jvm heap dump'> (saves JVM binary heap to $heap_file)";
  echo "</form>";

  if ($_POST['action'] == 'jvm heap dump') {
    $memory_mbean = $g_mbean_server->lookup("com.sun.management:type=HotSpotDiagnostic");

    $memory_mbean->dumpHeap($heap_file, true);

    echo "<h2>Saving Heap to $heap_file</h2>";
  }
  
  if ($_POST['action'] == 'dump heap') {
    $entries = $heap->dump();

    $is_heap_available = sizeof($entries) > 0;
  }
  else {
    $entries = $heap->getLastHeapDump();
    $is_heap_available = true;
  }
}

function display_heap_dump_subtab($name, $entries) 
{
  $total_size = 0;
  foreach ($entries as $entry) {
    $total_size += $entry->getTotalSize();
  }

  if (sizeof($entries) > 0) {
    echo "<div id='${name}-heap-dump-tab' class='heap-dump-tab'>\n";
    echo "<div class='pie-chart'>\n";
    echo "<canvas id='${name}-heap-dump-chart'/ width='300' height='300' style='display: block'></canvas>\n";
    echo "<div id='${name}-heap-dump-label' class='chart-label'></div>\n";
    echo "</div>\n";

    echo "<table id='${name}-heap-dump' class='heap-dump data'>";
    echo "<tr>\n";
    echo "  <th>% self</th>\n";
    echo "  <th>self+desc</th>\n";
    echo "  <th>self</th>\n";
    echo "  <th>desc</th>\n";
    echo "  <th>count</th>\n";
    echo "  <th colspan='2'>class</th>\n";
    echo "</tr>\n";

    for ($i = 0; $i < sizeof($entries); $i++) {
      $entry = $entries[$i];

      echo "<tr class='chart-data " . row_style($i) . "'>";
      echo "<td class='heap-percent-self'>";
      printf("%04.2f%%", 100 * $entry->getTotalSize() / $total_size);
      echo "</td>";
      echo "<td class='heap-self-plus-desc' align='right'>";
      printf("%.3fM", $entry->getTotalSize() / (1024 * 1024));
      echo "</td>";
      echo "<td class='heap-self' align='right'>";
      printf("%.3fM", $entry->getSelfSize() / (1024 * 1024));
      echo "</td>";
      echo "<td class='heap-desc' align='right'>";
      printf("%.3fM", $entry->getChildSize() / (1024 * 1024));
      echo "</td>";
      echo "<td class='heap-count' align='right'>";
      printf("%d", $entry->getCount());
      echo "</td>";
      echo "<td class='heap-class'>";
      echo "{$entry->getClassName()}";
      echo "</td>";
      echo "<td class='heap-legend'></td>";
      echo "</tr>\n";
    }

    echo "</table>\n";
    echo "</div>\n";
  }
}

if (sizeof($entries) > 0) {
  $application_entries = array();
  $jvm_entries = array();
  $resin_entries = array();

  foreach ($entries as $entry) {
    if (strpos($entry->getClassName(), "java") === 0
        || strpos($entry->getClassName(), "com.sun") === 0
        || strpos($entry->getClassName(), "sun.") === 0
        || ereg("^(boolean|int|byte|float|double|long|char|short)(\[\])*$", 
                $entry->getClassName())) {
      array_push($jvm_entries, $entry);
    }
    elseif (strpos($entry->getClassName(), "com.caucho") === 0) {
      array_push($resin_entries, $entry);
    }
    else {
      array_push($application_entries, $entry);
    }
  }


  echo "<div id='heap-tabs'>";
  echo "<ul>";
  if (count($application_entries) > 0)
    echo "<li><a href='#Application-heap-dump-tab'>Application</a></li>\n";
  if (count($jvm_entries) > 0)
    echo "<li><a href='#JVM-heap-dump-tab'>JVM</a></li>\n";
  if (count($resin_entries) > 0)
    echo "<li><a href='#Resin-heap-dump-tab'>Resin</a></li>\n";
  if (count($entries) > 0)
    echo "<li><a href='#All-heap-dump-tab'>All</a></li>\n";
  echo "</ul>";

  if (count($application_entries) > 0)
    display_heap_dump_subtab("Application", $application_entries);

  if (count($jvm_entries) > 0)
    display_heap_dump_subtab("JVM", $jvm_entries);

  if (count($resin_entries) > 0)
    display_heap_dump_subtab("Resin", $resin_entries);

  if (count($entries) > 0)
    display_heap_dump_subtab("All", $entries);
}
else if (! $is_heap_available) {
  echo "<h2>Heap dump is not available</h2>\n";
  echo "<p>The heap dump requires Resin Professional and compiled JNI. It\n";
  echo "also requires an '-agentlib:resin' JVM argument:</p>\n";

  echo "<pre>\n";
  echo "&lt;resin ...>\n";
  echo "  &lt;cluster id='...'>\n";
  echo "    &lt;server id='...'>\n";
  echo "       ...\n";
  echo "       &lt;jvm-arg>-agentlib:resin&lt;/jvm-arg>\n";
  echo "    &lt;/server>\n";
  echo "  &lt;/cluster>\n";
  echo "&lt;/resin>\n";
  echo "</pre>\n";
}

echo "</div>\n";
echo "</div>\n";

$javascript = javascript_create_tab("memory-tabs");

if ($_REQUEST["action"] == "dump heap"
    || $_REQUEST["action"] == "jvm heap dump") {
  $javascript .= "tabs.tabs('select', 1);\n";
}

if ($is_heap_available) {
  $javascript .= <<<EOF

var heapTabLinks = $("#heap-tabs > ul > li > a");
var heapTabs = $("#heap-tabs").tabs();

heapTabLinks.each(function(i) {
  var tabContents = $($(this).attr("href"));
  var table = tabContents.find("table.heap-dump");

  var builder = null;

  var buildChart = function() { 
    if (builder == null) { 
      builder = new PieChartBuilder();
      builder.setTable(table);
      builder.setLabelId(table.attr("id") + "-label");
      builder.setChartId(table.attr("id") + "-chart");
      builder.setLabelClass("heap-class");
      builder.setDataClass("heap-self-plus-desc");
      builder.setLegendClass("heap-legend");
      builder.setNumTopSlices(10);
      builder.create();
    }
  };

  if (heapTabs.tabs("option", "selected") == i) {
    buildChart();
  }
  else {
    tabs.bind('tabsselect', function(event, ui) {
      if (ui.index == i)
        buildChart();
    });
  }
});

EOF;
}

display_footer($g_page, $javascript);

?>
