Added trace attributes to properties:

TRACE_READ - log a message whenever the property is read.

TRACE_WRITE - log a message whenever the property is written.

The second one works only when the property value is changed through
the property manager; tied variables and accessors are not polled for
value changes because of the performance hit.

These methods end up invoking private methods
SGPropertyNode::trace_read and SGPropertyNode::trace_write.  By
setting breakpoints on these methods inside a debugger, it is possible
to debug property access and find what parts of a program are reading
or writing specific property values by doing a backtrace.

In the XML property files, users can use the attributes 'trace-read'
and 'trace-write' to control tracing; the value should be 'y' to
enable tracing or 'n' to disable it (the default).
This commit is contained in:
david
2001-12-12 02:28:28 +00:00
parent 019a9527d2
commit 6fa9f86d1c
3 changed files with 217 additions and 86 deletions

View File

@@ -7,6 +7,7 @@
// $Id$
#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
#include <stdio.h>
#include <stdlib.h>
@@ -42,6 +43,9 @@ public:
#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt
#define TEST_WRITE if (!getAttribute(WRITE)) return false
#define DO_TRACE_READ(type) if(getAttribute(TRACE_READ)) trace_read(type)
#define DO_TRACE_WRITE(type) if (getAttribute(TRACE_WRITE)) trace_write(type)
#define GET_BOOL (_value.bool_val->getValue())
#define GET_INT (_value.int_val->getValue())
#define GET_LONG (_value.long_val->getValue())
@@ -406,6 +410,66 @@ SGPropertyNode::clear_value ()
}
/**
* Get the value as a string.
*/
string
SGPropertyNode::get_string () const
{
TEST_READ("");
char buf[128];
switch (_type) {
case ALIAS:
return _value.alias->getStringValue();
case BOOL:
if (GET_BOOL)
return "true";
else
return "false";
case INT:
sprintf(buf, "%d", GET_INT);
return buf;
case LONG:
sprintf(buf, "%ld", GET_LONG);
return buf;
case FLOAT:
sprintf(buf, "%f", GET_FLOAT);
return buf;
case DOUBLE:
sprintf(buf, "%f", GET_DOUBLE);
return buf;
case STRING:
case UNSPECIFIED:
return GET_STRING;
}
return ""; // if NONE
}
/**
* Trace a read access for a property.
*/
void
SGPropertyNode::trace_read (SGPropertyNode::Type accessType) const
{
SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath()
<< ", value \"" << get_string() << '"');
}
/**
* Trace a write access for a property.
*/
void
SGPropertyNode::trace_write (SGPropertyNode::Type accessType) const
{
SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath()
<< ", value\"" << get_string() << '"');
}
/**
* Alias to another node.
*/
@@ -586,6 +650,7 @@ SGPropertyNode::getType () const
bool
SGPropertyNode::getBoolValue () const
{
DO_TRACE_READ(BOOL);
TEST_READ(false);
switch (_type) {
case ALIAS:
@@ -611,6 +676,7 @@ SGPropertyNode::getBoolValue () const
int
SGPropertyNode::getIntValue () const
{
DO_TRACE_READ(INT);
TEST_READ(0);
switch (_type) {
case ALIAS:
@@ -636,6 +702,7 @@ SGPropertyNode::getIntValue () const
long
SGPropertyNode::getLongValue () const
{
DO_TRACE_READ(LONG);
TEST_READ(0L);
switch (_type) {
case ALIAS:
@@ -661,6 +728,7 @@ SGPropertyNode::getLongValue () const
float
SGPropertyNode::getFloatValue () const
{
DO_TRACE_READ(FLOAT);
TEST_READ(0.0);
switch (_type) {
case ALIAS:
@@ -686,6 +754,7 @@ SGPropertyNode::getFloatValue () const
double
SGPropertyNode::getDoubleValue () const
{
DO_TRACE_READ(DOUBLE);
TEST_READ(0.0L);
switch (_type) {
case ALIAS:
@@ -711,40 +780,14 @@ SGPropertyNode::getDoubleValue () const
string
SGPropertyNode::getStringValue () const
{
TEST_READ("");
char buf[128];
switch (_type) {
case ALIAS:
return _value.alias->getStringValue();
case BOOL:
if (GET_BOOL)
return "true";
else
return "false";
case INT:
sprintf(buf, "%d", GET_INT);
return buf;
case LONG:
sprintf(buf, "%ld", GET_LONG);
return buf;
case FLOAT:
sprintf(buf, "%f", GET_FLOAT);
return buf;
case DOUBLE:
sprintf(buf, "%f", GET_DOUBLE);
return buf;
case STRING:
case UNSPECIFIED:
return GET_STRING;
}
return ""; // if NONE
DO_TRACE_READ(STRING);
return get_string();
}
bool
SGPropertyNode::setBoolValue (bool value)
{
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
clear_value();
@@ -754,27 +797,36 @@ SGPropertyNode::setBoolValue (bool value)
switch (_type) {
case ALIAS:
return _value.alias->setBoolValue(value);
result = _value.alias->setBoolValue(value);
break;
case BOOL:
return SET_BOOL(value);
result = SET_BOOL(value);
break;
case INT:
return SET_INT(int(value));
result = SET_INT(int(value));
break;
case LONG:
return SET_LONG(long(value));
result = SET_LONG(long(value));
break;
case FLOAT:
return SET_FLOAT(float(value));
result = SET_FLOAT(float(value));
break;
case DOUBLE:
return SET_DOUBLE(double(value));
result = SET_DOUBLE(double(value));
break;
case STRING:
return SET_STRING(value ? "true" : "false");
result = SET_STRING(value ? "true" : "false");
break;
}
return false; // should never happen
DO_TRACE_WRITE(BOOL);
return result;
}
bool
SGPropertyNode::setIntValue (int value)
{
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
clear_value();
@@ -784,30 +836,39 @@ SGPropertyNode::setIntValue (int value)
switch (_type) {
case ALIAS:
return _value.alias->setIntValue(value);
result = _value.alias->setIntValue(value);
break;
case BOOL:
return SET_BOOL(value == 0 ? false : true);
result = SET_BOOL(value == 0 ? false : true);
break;
case INT:
return SET_INT(value);
result = SET_INT(value);
break;
case LONG:
return SET_LONG(long(value));
result = SET_LONG(long(value));
break;
case FLOAT:
return SET_FLOAT(float(value));
result = SET_FLOAT(float(value));
break;
case DOUBLE:
return SET_DOUBLE(double(value));
result = SET_DOUBLE(double(value));
break;
case STRING: {
char buf[128];
sprintf(buf, "%d", value);
return SET_STRING(buf);
result = SET_STRING(buf);
break;
}
}
return false; // should never happen
DO_TRACE_WRITE(INT);
return result;
}
bool
SGPropertyNode::setLongValue (long value)
{
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
clear_value();
@@ -817,30 +878,39 @@ SGPropertyNode::setLongValue (long value)
switch (_type) {
case ALIAS:
return _value.alias->setLongValue(value);
result = _value.alias->setLongValue(value);
break;
case BOOL:
return SET_BOOL(value == 0L ? false : true);
result = SET_BOOL(value == 0L ? false : true);
break;
case INT:
return SET_INT(int(value));
result = SET_INT(int(value));
break;
case LONG:
return SET_LONG(value);
result = SET_LONG(value);
break;
case FLOAT:
return SET_FLOAT(float(value));
result = SET_FLOAT(float(value));
break;
case DOUBLE:
return SET_DOUBLE(double(value));
result = SET_DOUBLE(double(value));
break;
case STRING: {
char buf[128];
sprintf(buf, "%d", value);
return SET_STRING(buf);
result = SET_STRING(buf);
break;
}
}
return false; // should never happen
DO_TRACE_WRITE(LONG);
return result;
}
bool
SGPropertyNode::setFloatValue (float value)
{
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
clear_value();
@@ -850,30 +920,39 @@ SGPropertyNode::setFloatValue (float value)
switch (_type) {
case ALIAS:
return _value.alias->setFloatValue(value);
result = _value.alias->setFloatValue(value);
break;
case BOOL:
return SET_BOOL(value == 0.0 ? false : true);
result = SET_BOOL(value == 0.0 ? false : true);
break;
case INT:
return SET_INT(int(value));
result = SET_INT(int(value));
break;
case LONG:
return SET_LONG(long(value));
result = SET_LONG(long(value));
break;
case FLOAT:
return SET_FLOAT(value);
result = SET_FLOAT(value);
break;
case DOUBLE:
return SET_DOUBLE(double(value));
result = SET_DOUBLE(double(value));
break;
case STRING: {
char buf[128];
sprintf(buf, "%f", value);
return SET_STRING(buf);
result = SET_STRING(buf);
break;
}
}
return false; // should never happen
DO_TRACE_WRITE(FLOAT);
return result;
}
bool
SGPropertyNode::setDoubleValue (double value)
{
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
clear_value();
@@ -883,30 +962,39 @@ SGPropertyNode::setDoubleValue (double value)
switch (_type) {
case ALIAS:
return _value.alias->setDoubleValue(value);
result = _value.alias->setDoubleValue(value);
break;
case BOOL:
return SET_BOOL(value == 0.0L ? false : true);
result = SET_BOOL(value == 0.0L ? false : true);
break;
case INT:
return SET_INT(int(value));
result = SET_INT(int(value));
break;
case LONG:
return SET_LONG(long(value));
result = SET_LONG(long(value));
break;
case FLOAT:
return SET_FLOAT(float(value));
result = SET_FLOAT(float(value));
break;
case DOUBLE:
return SET_DOUBLE(value);
result = SET_DOUBLE(value);
break;
case STRING: {
char buf[128];
sprintf(buf, "%lf", value);
return SET_STRING(buf);
result = SET_STRING(buf);
break;
}
}
return false; // should never happen
DO_TRACE_WRITE(DOUBLE);
return result;
}
bool
SGPropertyNode::setStringValue (string value)
{
bool result = false;
TEST_WRITE;
if (_type == NONE || _type == UNSPECIFIED) {
clear_value();
@@ -916,27 +1004,36 @@ SGPropertyNode::setStringValue (string value)
switch (_type) {
case ALIAS:
return _value.alias->setStringValue(value);
result = _value.alias->setStringValue(value);
break;
case BOOL:
return SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false);
result = SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false);
break;
case INT:
return SET_INT(atoi(value.c_str()));
result = SET_INT(atoi(value.c_str()));
break;
case LONG:
return SET_LONG(strtol(value.c_str(), 0, 0));
result = SET_LONG(strtol(value.c_str(), 0, 0));
break;
case FLOAT:
return SET_FLOAT(atof(value.c_str()));
result = SET_FLOAT(atof(value.c_str()));
break;
case DOUBLE:
return SET_DOUBLE(strtod(value.c_str(), 0));
result = SET_DOUBLE(strtod(value.c_str(), 0));
break;
case STRING:
return SET_STRING(value);
result = SET_STRING(value);
break;
}
return false; // should never happen
DO_TRACE_WRITE(STRING);
return result;
}
bool
SGPropertyNode::setUnspecifiedValue (string value)
{
bool result = false;
TEST_WRITE;
if (_type == NONE) {
clear_value();
@@ -946,23 +1043,31 @@ SGPropertyNode::setUnspecifiedValue (string value)
switch (_type) {
case ALIAS:
return _value.alias->setUnspecifiedValue(value);
result = _value.alias->setUnspecifiedValue(value);
break;
case BOOL:
return SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false);
result = SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false);
break;
case INT:
return SET_INT(atoi(value.c_str()));
result = SET_INT(atoi(value.c_str()));
break;
case LONG:
return SET_LONG(strtol(value.c_str(), 0, 0));
result = SET_LONG(strtol(value.c_str(), 0, 0));
break;
case FLOAT:
return SET_FLOAT(atof(value.c_str()));
result = SET_FLOAT(atof(value.c_str()));
break;
case DOUBLE:
return SET_DOUBLE(strtod(value.c_str(), 0));
result = SET_DOUBLE(strtod(value.c_str(), 0));
break;
case STRING:
case UNSPECIFIED:
return SET_STRING(value);
result = SET_STRING(value);
break;
}
return false; // should never happen
DO_TRACE_WRITE(UNSPECIFIED);
return result;
}
bool

View File

@@ -511,7 +511,9 @@ public:
enum Attribute {
READ = 1,
WRITE = 2,
ARCHIVE = 4
ARCHIVE = 4,
TRACE_READ = 8,
TRACE_WRITE = 16
};
@@ -1038,6 +1040,24 @@ private:
*/
void clear_value ();
/**
* Get the value as a string.
*/
string get_string () const;
/**
* Trace a read access.
*/
void trace_read (Type accessType) const;
/**
* Trace a write access.
*/
void trace_write (Type accessType) const;
string _name;
int _index;
SGPropertyNode * _parent;

View File

@@ -181,6 +181,12 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
attval = atts.getValue("archive");
if (checkFlag(attval, false))
mode |= SGPropertyNode::ARCHIVE;
attval = atts.getValue("trace-read");
if (checkFlag(attval, false))
mode |= SGPropertyNode::TRACE_READ;
attval = atts.getValue("trace-write");
if (checkFlag(attval, false))
mode |= SGPropertyNode::TRACE_WRITE;
// Check for an alias.
attval = atts.getValue("alias");