genesis-3d_engine/Engine/foundation/debug/debugpagehandler.cc
zhongdaohuan 6e8fbca745 genesis-3d engine version 1.3.
match the genesis editor version 1.3.0.653.
2014-05-05 14:50:33 +08:00

437 lines
17 KiB
C++

/****************************************************************************
Copyright (c) 2008, Radon Labs GmbH
Copyright (c) 2011-2013,WebJet Business Division,CYOU
http://www.genesis-3d.com.cn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "stdneb.h"
#include "debug/debugpagehandler.h"
#include "http/html/htmlpagewriter.h"
#include "http/svg/svglinechartwriter.h"
#include "debug/debugserver.h"
#include "debug/debugtimer.h"
#include "debug/debugcounter.h"
#include "util/variant.h"
namespace Debug
{
__ImplementClass(Debug::DebugPageHandler, 'DBPH', Http::HttpRequestHandler);
using namespace Http;
using namespace Util;
using namespace Timing;
using namespace Math;
//------------------------------------------------------------------------------
/**
*/
DebugPageHandler::DebugPageHandler():
sortByColumn("Name")
{
this->SetName("Debug");
this->SetDesc("show debug subsystem information");
this->SetRootLocation("debug");
}
//------------------------------------------------------------------------------
/**
*/
void
DebugPageHandler::HandleRequest(const GPtr<HttpRequest>& request)
{
n_assert(HttpMethod::Get == request->GetMethod());
// first check if a command has been defined in the URI
Dictionary<String,String> query = request->GetURI().ParseQuery();
if (query.Contains("timer"))
{
this->HandleTimerRequest(query["timer"], request);
return;
}
else if (query.Contains("counter"))
{
this->HandleCounterRequest(query["counter"], request);
return;
}
else if (query.Contains("timerChart"))
{
this->HandleTimerChartRequest(query["timerChart"], request);
return;
}
else if (query.Contains("counterChart"))
{
this->HandleCounterChartRequest(query["counterChart"], request);
return;
}
else if (query.Contains("TimerTableSort"))
{
this->HandleTableSortRequest(query["TimerTableSort"], request);
}
// no command, display root page
GPtr<HtmlPageWriter> htmlWriter = HtmlPageWriter::Create();
htmlWriter->SetStream(request->GetResponseContentStream());
htmlWriter->SetTitle("Genesis Debug Subsystem Info");
if (htmlWriter->Open())
{
htmlWriter->Element(HtmlElement::Heading1, "Debug Subsystem");
htmlWriter->AddAttr("href", "/index.html");
htmlWriter->Element(HtmlElement::Anchor, "Home");
// display debug timers
htmlWriter->Element(HtmlElement::Heading3, "Debug Timers");
htmlWriter->AddAttr("border", "1");
htmlWriter->AddAttr("rules", "cols");
htmlWriter->Begin(HtmlElement::Table);
htmlWriter->AddAttr("bgcolor", "lightsteelblue");
htmlWriter->Begin(HtmlElement::TableRow);
htmlWriter->Begin(HtmlElement::TableData);
htmlWriter->AddAttr("href", "/debug?TimerTableSort=Name");
htmlWriter->Element(HtmlElement::Anchor, "Name");
htmlWriter->End(HtmlElement::TableData);
htmlWriter->Begin(HtmlElement::TableData);
htmlWriter->AddAttr("href", "/debug?TimerTableSort=Min");
htmlWriter->Element(HtmlElement::Anchor, "Min");
htmlWriter->End(HtmlElement::TableData);
htmlWriter->Begin(HtmlElement::TableData);
htmlWriter->AddAttr("href", "/debug?TimerTableSort=Max");
htmlWriter->Element(HtmlElement::Anchor, "Max");
htmlWriter->End(HtmlElement::TableData);
htmlWriter->Begin(HtmlElement::TableData);
htmlWriter->AddAttr("href", "/debug?TimerTableSort=Avg");
htmlWriter->Element(HtmlElement::Anchor, "Avg");
htmlWriter->End(HtmlElement::TableData);
htmlWriter->End(HtmlElement::TableRow);
// iterate through all debug timers
Array<GPtr<DebugTimer> > debugTimers = DebugServer::Instance()->GetDebugTimers();
// copy to dictionary for sort
Dictionary<Variant, GPtr<DebugTimer> > sortedTimer;
sortedTimer.BeginBulkAdd();
IndexT idx;
for (idx = 0; idx < debugTimers.Size(); ++idx)
{
Array<Time> history = debugTimers[idx]->GetHistory();
Time minTime, maxTime, avgTime;
this->ComputeMinMaxAvgTimes(history, minTime, maxTime, avgTime);
if (this->sortByColumn == "Name")
{
sortedTimer.Add(debugTimers[idx]->GetName().Value(), debugTimers[idx]);
}
else if (this->sortByColumn == "Min")
{
sortedTimer.Add(float(minTime), debugTimers[idx]);
}
else if (this->sortByColumn == "Max")
{
sortedTimer.Add(float(maxTime), debugTimers[idx]);
}
else if (this->sortByColumn == "Avg")
{
sortedTimer.Add(float(avgTime), debugTimers[idx]);
}
}
sortedTimer.EndBulkAdd();
debugTimers = sortedTimer.ValuesAsArray();
IndexT i;
for (i = 0; i < debugTimers.Size(); i++)
{
StringAtom name = debugTimers[i]->GetName();
Array<Time> history = debugTimers[i]->GetHistory();
Time minTime, maxTime, avgTime;
this->ComputeMinMaxAvgTimes(history, minTime, maxTime, avgTime);
htmlWriter->Begin(HtmlElement::TableRow);
htmlWriter->Begin(HtmlElement::TableData);
htmlWriter->AddAttr("href", "/debug?timer=" + name.AsString());
htmlWriter->Element(HtmlElement::Anchor, name.AsString());
htmlWriter->End(HtmlElement::TableData);
htmlWriter->Element(HtmlElement::TableData, String::FromFloat(float(minTime)));
htmlWriter->Element(HtmlElement::TableData, String::FromFloat(float(maxTime)));
htmlWriter->Element(HtmlElement::TableData, String::FromFloat(float(avgTime)));
htmlWriter->End(HtmlElement::TableRow);
}
htmlWriter->End(HtmlElement::Table);
// display debug counters
htmlWriter->Element(HtmlElement::Heading3, "Debug Counters");
htmlWriter->AddAttr("border", "1");
htmlWriter->AddAttr("rules", "cols");
htmlWriter->Begin(HtmlElement::Table);
htmlWriter->AddAttr("bgcolor", "lightsteelblue");
htmlWriter->Begin(HtmlElement::TableRow);
htmlWriter->Element(HtmlElement::TableHeader, "Name");
htmlWriter->Element(HtmlElement::TableHeader, "Min");
htmlWriter->Element(HtmlElement::TableHeader, "Max");
htmlWriter->Element(HtmlElement::TableHeader, "Avg");
htmlWriter->End(HtmlElement::TableRow);
// iterate through all debug counters
Array<GPtr<DebugCounter> > debugCounters = DebugServer::Instance()->GetDebugCounters();
for (i = 0; i < debugCounters.Size(); i++)
{
StringAtom name = debugCounters[i]->GetName();
Array<int> history = debugCounters[i]->GetHistory();
int minCount, maxCount, avgCount;
this->ComputeMinMaxAvgCounts(history, minCount, maxCount, avgCount);
htmlWriter->Begin(HtmlElement::TableRow);
htmlWriter->Begin(HtmlElement::TableData);
htmlWriter->AddAttr("href", "/debug?counter=" + name.AsString());
htmlWriter->Element(HtmlElement::Anchor, name.Value());
htmlWriter->End(HtmlElement::TableData);
htmlWriter->Element(HtmlElement::TableData, String::FromInt(minCount));
htmlWriter->Element(HtmlElement::TableData, String::FromInt(maxCount));
htmlWriter->Element(HtmlElement::TableData, String::FromInt(avgCount));
htmlWriter->End(HtmlElement::TableRow);
}
htmlWriter->End(HtmlElement::Table);
htmlWriter->Close();
request->SetStatus(HttpStatus::OK);
}
else
{
request->SetStatus(HttpStatus::InternalServerError);
}
}
//------------------------------------------------------------------------------
/**
Gets the min/max/avg time from an array of Time samples.
*/
void
DebugPageHandler::ComputeMinMaxAvgTimes(const Array<Time>& times, Time& outMin, Time& outMax, Time& outAvg) const
{
if (times.Size() > 0)
{
outMin = 10000000.0f;
outMax = -10000000.0f;
outAvg = 0.0;
IndexT i;
for (i = 0; i < times.Size(); i++)
{
outMin = n_min(float(outMin), float(times[i]));
outMax = n_max(float(outMax), float(times[i]));
outAvg += times[i];
}
outAvg /= times.Size();
}
else
{
outMin = 0.0;
outMax = 0.0;
outAvg = 0.0;
}
}
//------------------------------------------------------------------------------
/**
Gets the min/max/avg counter values from an array of counter samples.
*/
void
DebugPageHandler::ComputeMinMaxAvgCounts(const Array<int>& counterValues, int& outMin, int& outMax, int& outAvg) const
{
if (counterValues.Size() > 0)
{
outMin = (1<<30);
outMax = -(1<<30);
outAvg = 0;
IndexT i;
for (i = 0; i < counterValues.Size(); i++)
{
outMin = n_min(outMin, counterValues[i]);
outMax = n_max(outMax, counterValues[i]);
outAvg += counterValues[i];
}
outAvg /= counterValues.Size();
}
else
{
outMin = 0;
outMax = 0;
outAvg = 0;
}
}
//------------------------------------------------------------------------------
/**
Handles a HTTP request for a specific debug timer.
*/
void
DebugPageHandler::HandleTimerRequest(const String& timerName, const GPtr<HttpRequest>& request)
{
GPtr<HtmlPageWriter> htmlWriter = HtmlPageWriter::Create();
htmlWriter->SetStream(request->GetResponseContentStream());
htmlWriter->SetTitle("Genesis Debug Timer Info");
if (htmlWriter->Open())
{
htmlWriter->Element(HtmlElement::Heading1, "Timer: " + timerName);
htmlWriter->AddAttr("href", "/index.html");
htmlWriter->Element(HtmlElement::Anchor, "Home");
htmlWriter->LineBreak();
htmlWriter->AddAttr("href", "/debug");
htmlWriter->Element(HtmlElement::Anchor, "Debug Subsystem Home");
htmlWriter->LineBreak();
htmlWriter->AddAttr("data", "/debug?timerChart=" + timerName);
htmlWriter->Element(HtmlElement::Object, timerName);
htmlWriter->Close();
request->SetStatus(HttpStatus::OK);
}
else
{
request->SetStatus(HttpStatus::InternalServerError);
}
}
//------------------------------------------------------------------------------
/**
Handles a HTTP request for a specific debug counter.
*/
void
DebugPageHandler::HandleCounterRequest(const String& counterName, const GPtr<HttpRequest>& request)
{
GPtr<HtmlPageWriter> htmlWriter = HtmlPageWriter::Create();
htmlWriter->SetStream(request->GetResponseContentStream());
htmlWriter->SetTitle("Genesis Debug Counter Info");
if (htmlWriter->Open())
{
htmlWriter->Element(HtmlElement::Heading1, "Counter: " + counterName);
htmlWriter->AddAttr("href", "/index.html");
htmlWriter->Element(HtmlElement::Anchor, "Home");
htmlWriter->LineBreak();
htmlWriter->AddAttr("href", "/debug");
htmlWriter->Element(HtmlElement::Anchor, "Debug Subsystem Home");
htmlWriter->LineBreak();
htmlWriter->AddAttr("data", "/debug?counterChart=" + counterName);
htmlWriter->Element(HtmlElement::Object, counterName);
htmlWriter->Close();
request->SetStatus(HttpStatus::OK);
}
else
{
request->SetStatus(HttpStatus::InternalServerError);
}
}
//------------------------------------------------------------------------------
/**
Writes an SVG chart for a debug timer.
*/
void
DebugPageHandler::HandleTimerChartRequest(const String& timerName, const GPtr<HttpRequest>& request)
{
// setup a SVG line chart writer
GPtr<DebugTimer> debugTimer = DebugServer::Instance()->GetDebugTimerByName(timerName);
if (debugTimer.isvalid())
{
GPtr<SvgLineChartWriter> writer = SvgLineChartWriter::Create();
writer->SetStream(request->GetResponseContentStream());
writer->SetCanvasDimensions(1024, 256);
if (writer->Open())
{
// get min/max/avg times, convert time to float array
Array<Time> timeArray = debugTimer->GetHistory();
Time minTime, maxTime, avgTime;
this->ComputeMinMaxAvgTimes(timeArray, minTime, maxTime, avgTime);
Array<float> floatArray(timeArray.Size(), 0);
IndexT i;
for (i = 0; i < timeArray.Size(); i++)
{
floatArray.Append(float(timeArray[i]));
}
// setup the svg chart writer and draw the chart
writer->SetupXAxis("frames", "frames", -int(floatArray.Size()), 0);
writer->SetupYAxis("samples", "samples", 0.0f, float(maxTime));
writer->AddTrack("samples", "red", floatArray);
writer->Draw();
writer->Close();
}
request->SetStatus(HttpStatus::OK);
}
else
{
request->SetStatus(HttpStatus::NotFound);
}
}
//------------------------------------------------------------------------------
/**
Writes an SVG chart for a debug counter.
*/
void
DebugPageHandler::HandleCounterChartRequest(const String& counterName, const GPtr<HttpRequest>& request)
{
// setup a SVG line chart writer
GPtr<DebugCounter> debugCounter = DebugServer::Instance()->GetDebugCounterByName(counterName);
if (debugCounter.isvalid())
{
GPtr<SvgLineChartWriter> writer = SvgLineChartWriter::Create();
writer->SetStream(request->GetResponseContentStream());
writer->SetCanvasDimensions(1024, 256);
if (writer->Open())
{
// get min/max/avg values, convert int to float array
// FIXME: SvgLineChartWriter should also accept integer tracks!
Array<int> intArray = debugCounter->GetHistory();
if (intArray.Size() > 0)
{
int minVal, maxVal, avgVal;
this->ComputeMinMaxAvgCounts(intArray, minVal, maxVal, avgVal);
// for maxVal == minVal set valid maxVal
if (minVal == maxVal) maxVal = minVal + 1;
Array<float> floatArray(intArray.Size(), 0);
IndexT i;
for (i = 0; i < intArray.Size(); i++)
{
floatArray.Append(float(intArray[i]));
}
// setup the svg chart writer and draw the chart
writer->SetupXAxis("frames", "frames", -int(floatArray.Size()), 0);
writer->SetupYAxis("samples", "samples", 0.0f, float(maxVal));
writer->AddTrack("samples", "red", floatArray);
writer->Draw();
}
writer->Close();
}
request->SetStatus(HttpStatus::OK);
}
else
{
request->SetStatus(HttpStatus::NotFound);
}
}
//------------------------------------------------------------------------------
/**
*/
void
DebugPageHandler::HandleTableSortRequest(const Util::String& columnName, const GPtr<Http::HttpRequest>& request)
{
this->sortByColumn = columnName;
request->SetStatus(HttpStatus::OK);
}
} // namespace Debug