aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sartain <mikesart@valvesoftware.com>2013-07-30 16:44:36 +0000
committerMichael Sartain <mikesart@valvesoftware.com>2013-07-30 16:44:36 +0000
commit4b93aee16f3a81aaf18114c88cc52482262035f3 (patch)
treee479e16291c2cc81565de8d787a00f1ce1ec7ad7
parent0b864ee64c72ea578f7351a9d6284ed5d712b9f0 (diff)
downloadlldb-4b93aee16f3a81aaf18114c88cc52482262035f3.tar.gz
Add format specifiers to various format ids so we can print thread ids in decimal on Linux and FreeBSD.
CC: emaste Differential Revision: http://llvm-reviews.chandlerc.com/D1234 git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@187425 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--source/Core/Debugger.cpp276
-rw-r--r--source/Interpreter/ScriptInterpreterPython.cpp4
2 files changed, 169 insertions, 111 deletions
diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp
index f3ee4a85d..d1d2ebb05 100644
--- a/source/Core/Debugger.cpp
+++ b/source/Core/Debugger.cpp
@@ -99,12 +99,12 @@ g_language_enumerators[] =
#define MODULE_WITH_FUNC "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}"
#define FILE_AND_LINE "{ at ${line.file.basename}:${line.number}}"
-#define DEFAULT_THREAD_FORMAT "thread #${thread.index}: tid = ${thread.id}"\
+#define DEFAULT_THREAD_FORMAT "thread #${thread.index}: tid = ${thread.id%tid}"\
"{, ${frame.pc}}"\
MODULE_WITH_FUNC\
FILE_AND_LINE\
- "{, name = '${thread.name}}"\
- "{, queue = '${thread.queue}}"\
+ "{, name = '${thread.name}'}"\
+ "{, queue = '${thread.queue}'}"\
"{, stop reason = ${thread.stop-reason}}"\
"{\\nReturn value: ${thread.return-value}}"\
"\\n"
@@ -1326,6 +1326,27 @@ ScanBracketedRange (const char* var_name_begin,
return true;
}
+template <typename T>
+static bool RunScriptFormatKeyword(Stream &s, ScriptInterpreter *script_interpreter, T t, const std::string& script_name)
+{
+ if (script_interpreter)
+ {
+ Error script_error;
+ std::string script_output;
+
+ if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), t, script_output, script_error) && script_error.Success())
+ {
+ s.Printf("%s", script_output.c_str());
+ return true;
+ }
+ else
+ {
+ s.Printf("<error: %s>",script_error.AsCString());
+ }
+ }
+ return false;
+}
+
static ValueObjectSP
ExpandIndexedExpression (ValueObject* valobj,
size_t index,
@@ -1366,6 +1387,82 @@ ExpandIndexedExpression (ValueObject* valobj,
return item;
}
+static inline bool
+IsToken(const char *var_name_begin, const char *var)
+{
+ return (::strncmp (var_name_begin, var, strlen(var)) == 0);
+}
+
+static bool
+IsTokenWithFormat(const char *var_name_begin, const char *var, std::string &format, const char *default_format,
+ const ExecutionContext *exe_ctx_ptr, const SymbolContext *sc_ptr)
+{
+ int var_len = strlen(var);
+ if (::strncmp (var_name_begin, var, var_len) == 0)
+ {
+ var_name_begin += var_len;
+ if (*var_name_begin == '}')
+ {
+ format = default_format;
+ return true;
+ }
+ else if (*var_name_begin == '%')
+ {
+ // Allow format specifiers: x|X|u with optional width specifiers.
+ // ${thread.id%x} ; hex
+ // ${thread.id%X} ; uppercase hex
+ // ${thread.id%u} ; unsigned decimal
+ // ${thread.id%8.8X} ; width.precision + specifier
+ // ${thread.id%tid} ; unsigned on FreeBSD/Linux, otherwise default_format (0x%4.4x for thread.id)
+ int dot_count = 0;
+ const char *specifier = NULL;
+ int width_precision_length = 0;
+ const char *width_precision = ++var_name_begin;
+ while (isdigit(*var_name_begin) || *var_name_begin == '.')
+ {
+ dot_count += (*var_name_begin == '.');
+ if (dot_count > 1)
+ break;
+ var_name_begin++;
+ width_precision_length++;
+ }
+
+ if (IsToken (var_name_begin, "tid}"))
+ {
+ Target *target = Target::GetTargetFromContexts (exe_ctx_ptr, sc_ptr);
+ if (target)
+ {
+ ArchSpec arch (target->GetArchitecture ());
+ llvm::Triple::OSType ostype = arch.IsValid() ? arch.GetTriple().getOS() : llvm::Triple::UnknownOS;
+ if ((ostype == llvm::Triple::FreeBSD) || (ostype == llvm::Triple::Linux))
+ specifier = PRIu64;
+ }
+ if (!specifier)
+ {
+ format = default_format;
+ return true;
+ }
+ }
+ else if (IsToken (var_name_begin, "x}"))
+ specifier = PRIx64;
+ else if (IsToken (var_name_begin, "X}"))
+ specifier = PRIX64;
+ else if (IsToken (var_name_begin, "u}"))
+ specifier = PRIu64;
+
+ if (specifier)
+ {
+ format = "%";
+ if (width_precision_length)
+ format += std::string(width_precision, width_precision_length);
+ format += specifier;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
static bool
FormatPromptRecurse
(
@@ -1449,6 +1546,7 @@ FormatPromptRecurse
if (success)
{
const char *cstr = NULL;
+ std::string token_format;
Address format_addr;
bool calculate_format_addr_function_offset = false;
// Set reg_kind and reg_num to invalid values
@@ -1522,13 +1620,13 @@ FormatPromptRecurse
if (!valobj) break;
// simplest case ${var}, just print valobj's value
- if (::strncmp (var_name_begin, "var}", strlen("var}")) == 0)
+ if (IsToken (var_name_begin, "var}"))
{
was_plain_var = true;
target = valobj;
val_obj_display = ValueObject::eValueObjectRepresentationStyleValue;
}
- else if (::strncmp(var_name_begin,"var%",strlen("var%")) == 0)
+ else if (IsToken (var_name_begin,"var%"))
{
was_var_format = true;
// this is a variable with some custom format applied to it
@@ -1543,9 +1641,9 @@ FormatPromptRecurse
&val_obj_display);
}
// this is ${var.something} or multiple .something nested
- else if (::strncmp (var_name_begin, "var", strlen("var")) == 0)
+ else if (IsToken (var_name_begin, "var"))
{
- if (::strncmp(var_name_begin, "var[", strlen("var[")) == 0)
+ if (IsToken (var_name_begin, "var["))
was_var_indexed = true;
const char* percent_position;
ScanFormatDescriptor (var_name_begin,
@@ -1770,7 +1868,7 @@ FormatPromptRecurse
}
break;
case 'a':
- if (::strncmp (var_name_begin, "addr}", strlen("addr}")) == 0)
+ if (IsToken (var_name_begin, "addr}"))
{
if (addr && addr->IsValid())
{
@@ -1781,7 +1879,7 @@ FormatPromptRecurse
break;
case 'p':
- if (::strncmp (var_name_begin, "process.", strlen("process.")) == 0)
+ if (IsToken (var_name_begin, "process."))
{
if (exe_ctx)
{
@@ -1789,14 +1887,14 @@ FormatPromptRecurse
if (process)
{
var_name_begin += ::strlen ("process.");
- if (::strncmp (var_name_begin, "id}", strlen("id}")) == 0)
+ if (IsTokenWithFormat (var_name_begin, "id", token_format, "%" PRIu64, exe_ctx, sc))
{
- s.Printf("%" PRIu64, process->GetID());
+ s.Printf(token_format.c_str(), process->GetID());
var_success = true;
}
- else if ((::strncmp (var_name_begin, "name}", strlen("name}")) == 0) ||
- (::strncmp (var_name_begin, "file.basename}", strlen("file.basename}")) == 0) ||
- (::strncmp (var_name_begin, "file.fullpath}", strlen("file.fullpath}")) == 0))
+ else if ((IsToken (var_name_begin, "name}")) ||
+ (IsToken (var_name_begin, "file.basename}")) ||
+ (IsToken (var_name_begin, "file.fullpath}")))
{
Module *exe_module = process->GetTarget().GetExecutableModulePointer();
if (exe_module)
@@ -1813,23 +1911,13 @@ FormatPromptRecurse
}
}
}
- else if (::strncmp(var_name_begin, "script:", strlen("script:")) == 0)
+ else if (IsToken (var_name_begin, "script:"))
{
var_name_begin += ::strlen("script:");
std::string script_name(var_name_begin,var_name_end);
ScriptInterpreter* script_interpreter = process->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
- if (script_interpreter)
- {
- std::string script_output;
- Error script_error;
- if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), process,script_output,script_error) && script_error.Success())
- {
- s.Printf("%s", script_output.c_str());
- var_success = true;
- }
- else
- s.Printf("<error: %s>",script_error.AsCString());
- }
+ if (RunScriptFormatKeyword (s, script_interpreter, process, script_name))
+ var_success = true;
}
}
}
@@ -1837,7 +1925,7 @@ FormatPromptRecurse
break;
case 't':
- if (::strncmp (var_name_begin, "thread.", strlen("thread.")) == 0)
+ if (IsToken (var_name_begin, "thread."))
{
if (exe_ctx)
{
@@ -1845,36 +1933,36 @@ FormatPromptRecurse
if (thread)
{
var_name_begin += ::strlen ("thread.");
- if (::strncmp (var_name_begin, "id}", strlen("id}")) == 0)
+ if (IsTokenWithFormat (var_name_begin, "id", token_format, "0x%4.4" PRIx64, exe_ctx, sc))
{
- s.Printf("0x%4.4" PRIx64, thread->GetID());
+ s.Printf(token_format.c_str(), thread->GetID());
var_success = true;
}
- else if (::strncmp (var_name_begin, "protocol_id}", strlen("protocol_id}")) == 0)
+ else if (IsTokenWithFormat (var_name_begin, "protocol_id", token_format, "0x%4.4" PRIx64, exe_ctx, sc))
{
- s.Printf("0x%4.4" PRIx64, thread->GetProtocolID());
+ s.Printf(token_format.c_str(), thread->GetProtocolID());
var_success = true;
}
- else if (::strncmp (var_name_begin, "index}", strlen("index}")) == 0)
+ else if (IsTokenWithFormat (var_name_begin, "index", token_format, "%" PRIu64, exe_ctx, sc))
{
- s.Printf("%u", thread->GetIndexID());
+ s.Printf(token_format.c_str(), (uint64_t)thread->GetIndexID());
var_success = true;
}
- else if (::strncmp (var_name_begin, "name}", strlen("name}")) == 0)
+ else if (IsToken (var_name_begin, "name}"))
{
cstr = thread->GetName();
var_success = cstr && cstr[0];
if (var_success)
s.PutCString(cstr);
}
- else if (::strncmp (var_name_begin, "queue}", strlen("queue}")) == 0)
+ else if (IsToken (var_name_begin, "queue}"))
{
cstr = thread->GetQueueName();
var_success = cstr && cstr[0];
if (var_success)
s.PutCString(cstr);
}
- else if (::strncmp (var_name_begin, "stop-reason}", strlen("stop-reason}")) == 0)
+ else if (IsToken (var_name_begin, "stop-reason}"))
{
StopInfoSP stop_info_sp = thread->GetStopInfo ();
if (stop_info_sp && stop_info_sp->IsValid())
@@ -1887,7 +1975,7 @@ FormatPromptRecurse
}
}
}
- else if (::strncmp (var_name_begin, "return-value}", strlen("return-value}")) == 0)
+ else if (IsToken (var_name_begin, "return-value}"))
{
StopInfoSP stop_info_sp = thread->GetStopInfo ();
if (stop_info_sp && stop_info_sp->IsValid())
@@ -1900,28 +1988,18 @@ FormatPromptRecurse
}
}
}
- else if (::strncmp(var_name_begin, "script:", strlen("script:")) == 0)
+ else if (IsToken (var_name_begin, "script:"))
{
var_name_begin += ::strlen("script:");
std::string script_name(var_name_begin,var_name_end);
ScriptInterpreter* script_interpreter = thread->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
- if (script_interpreter)
- {
- std::string script_output;
- Error script_error;
- if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), thread,script_output,script_error) && script_error.Success())
- {
- s.Printf("%s", script_output.c_str());
- var_success = true;
- }
- else
- s.Printf("<error: %s>",script_error.AsCString());
- }
+ if (RunScriptFormatKeyword (s, script_interpreter, thread, script_name))
+ var_success = true;
}
}
}
}
- else if (::strncmp (var_name_begin, "target.", strlen("target.")) == 0)
+ else if (IsToken (var_name_begin, "target."))
{
// TODO: hookup properties
// if (!target_properties_sp)
@@ -1950,7 +2028,7 @@ FormatPromptRecurse
if (target)
{
var_name_begin += ::strlen ("target.");
- if (::strncmp (var_name_begin, "arch}", strlen("arch}")) == 0)
+ if (IsToken (var_name_begin, "arch}"))
{
ArchSpec arch (target->GetArchitecture ());
if (arch.IsValid())
@@ -1959,23 +2037,13 @@ FormatPromptRecurse
var_success = true;
}
}
- else if (::strncmp(var_name_begin, "script:", strlen("script:")) == 0)
+ else if (IsToken (var_name_begin, "script:"))
{
var_name_begin += ::strlen("script:");
std::string script_name(var_name_begin,var_name_end);
ScriptInterpreter* script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
- if (script_interpreter)
- {
- std::string script_output;
- Error script_error;
- if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), target,script_output,script_error) && script_error.Success())
- {
- s.Printf("%s", script_output.c_str());
- var_success = true;
- }
- else
- s.Printf("<error: %s>",script_error.AsCString());
- }
+ if (RunScriptFormatKeyword (s, script_interpreter, target, script_name))
+ var_success = true;
}
}
}
@@ -1983,25 +2051,25 @@ FormatPromptRecurse
case 'm':
- if (::strncmp (var_name_begin, "module.", strlen("module.")) == 0)
+ if (IsToken (var_name_begin, "module."))
{
if (sc && sc->module_sp.get())
{
Module *module = sc->module_sp.get();
var_name_begin += ::strlen ("module.");
- if (::strncmp (var_name_begin, "file.", strlen("file.")) == 0)
+ if (IsToken (var_name_begin, "file."))
{
if (module->GetFileSpec())
{
var_name_begin += ::strlen ("file.");
- if (::strncmp (var_name_begin, "basename}", strlen("basename}")) == 0)
+ if (IsToken (var_name_begin, "basename}"))
{
format_file_spec.GetFilename() = module->GetFileSpec().GetFilename();
var_success = format_file_spec;
}
- else if (::strncmp (var_name_begin, "fullpath}", strlen("fullpath}")) == 0)
+ else if (IsToken (var_name_begin, "fullpath}"))
{
format_file_spec = module->GetFileSpec();
var_success = format_file_spec;
@@ -2014,25 +2082,25 @@ FormatPromptRecurse
case 'f':
- if (::strncmp (var_name_begin, "file.", strlen("file.")) == 0)
+ if (IsToken (var_name_begin, "file."))
{
if (sc && sc->comp_unit != NULL)
{
var_name_begin += ::strlen ("file.");
- if (::strncmp (var_name_begin, "basename}", strlen("basename}")) == 0)
+ if (IsToken (var_name_begin, "basename}"))
{
format_file_spec.GetFilename() = sc->comp_unit->GetFilename();
var_success = format_file_spec;
}
- else if (::strncmp (var_name_begin, "fullpath}", strlen("fullpath}")) == 0)
+ else if (IsToken (var_name_begin, "fullpath}"))
{
format_file_spec = *sc->comp_unit;
var_success = format_file_spec;
}
}
}
- else if (::strncmp (var_name_begin, "frame.", strlen("frame.")) == 0)
+ else if (IsToken (var_name_begin, "frame."))
{
if (exe_ctx)
{
@@ -2040,36 +2108,36 @@ FormatPromptRecurse
if (frame)
{
var_name_begin += ::strlen ("frame.");
- if (::strncmp (var_name_begin, "index}", strlen("index}")) == 0)
+ if (IsToken (var_name_begin, "index}"))
{
s.Printf("%u", frame->GetFrameIndex());
var_success = true;
}
- else if (::strncmp (var_name_begin, "pc}", strlen("pc}")) == 0)
+ else if (IsToken (var_name_begin, "pc}"))
{
reg_kind = eRegisterKindGeneric;
reg_num = LLDB_REGNUM_GENERIC_PC;
var_success = true;
}
- else if (::strncmp (var_name_begin, "sp}", strlen("sp}")) == 0)
+ else if (IsToken (var_name_begin, "sp}"))
{
reg_kind = eRegisterKindGeneric;
reg_num = LLDB_REGNUM_GENERIC_SP;
var_success = true;
}
- else if (::strncmp (var_name_begin, "fp}", strlen("fp}")) == 0)
+ else if (IsToken (var_name_begin, "fp}"))
{
reg_kind = eRegisterKindGeneric;
reg_num = LLDB_REGNUM_GENERIC_FP;
var_success = true;
}
- else if (::strncmp (var_name_begin, "flags}", strlen("flags}")) == 0)
+ else if (IsToken (var_name_begin, "flags}"))
{
reg_kind = eRegisterKindGeneric;
reg_num = LLDB_REGNUM_GENERIC_FLAGS;
var_success = true;
}
- else if (::strncmp (var_name_begin, "reg.", strlen ("reg.")) == 0)
+ else if (IsToken (var_name_begin, "reg."))
{
reg_ctx = frame->GetRegisterContext().get();
if (reg_ctx)
@@ -2084,33 +2152,23 @@ FormatPromptRecurse
}
}
}
- else if (::strncmp(var_name_begin, "script:", strlen("script:")) == 0)
+ else if (IsToken (var_name_begin, "script:"))
{
var_name_begin += ::strlen("script:");
std::string script_name(var_name_begin,var_name_end);
ScriptInterpreter* script_interpreter = frame->GetThread()->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
- if (script_interpreter)
- {
- std::string script_output;
- Error script_error;
- if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), frame,script_output,script_error) && script_error.Success())
- {
- s.Printf("%s", script_output.c_str());
- var_success = true;
- }
- else
- s.Printf("<error: %s>",script_error.AsCString());
- }
+ if (RunScriptFormatKeyword (s, script_interpreter, frame, script_name))
+ var_success = true;
}
}
}
}
- else if (::strncmp (var_name_begin, "function.", strlen("function.")) == 0)
+ else if (IsToken (var_name_begin, "function."))
{
if (sc && (sc->function != NULL || sc->symbol != NULL))
{
var_name_begin += ::strlen ("function.");
- if (::strncmp (var_name_begin, "id}", strlen("id}")) == 0)
+ if (IsToken (var_name_begin, "id}"))
{
if (sc->function)
s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID());
@@ -2119,7 +2177,7 @@ FormatPromptRecurse
var_success = true;
}
- else if (::strncmp (var_name_begin, "name}", strlen("name}")) == 0)
+ else if (IsToken (var_name_begin, "name}"))
{
if (sc->function)
cstr = sc->function->GetName().AsCString (NULL);
@@ -2145,7 +2203,7 @@ FormatPromptRecurse
var_success = true;
}
}
- else if (::strncmp (var_name_begin, "name-with-args}", strlen("name-with-args}")) == 0)
+ else if (IsToken (var_name_begin, "name-with-args}"))
{
// Print the function name with arguments in it
@@ -2193,7 +2251,7 @@ FormatPromptRecurse
const char *close_paren = NULL;
if (open_paren)
{
- if (strncmp(open_paren, "(anonymous namespace)", strlen("(anonymous namespace)")) == 0)
+ if (IsToken (open_paren, "(anonymous namespace)"))
{
open_paren = strchr (open_paren + strlen("(anonymous namespace)"), '(');
if (open_paren)
@@ -2252,7 +2310,7 @@ FormatPromptRecurse
}
}
}
- else if (::strncmp (var_name_begin, "addr-offset}", strlen("addr-offset}")) == 0)
+ else if (IsToken (var_name_begin, "addr-offset}"))
{
var_success = addr != NULL;
if (var_success)
@@ -2261,7 +2319,7 @@ FormatPromptRecurse
calculate_format_addr_function_offset = true;
}
}
- else if (::strncmp (var_name_begin, "line-offset}", strlen("line-offset}")) == 0)
+ else if (IsToken (var_name_begin, "line-offset}"))
{
var_success = sc->line_entry.range.GetBaseAddress().IsValid();
if (var_success)
@@ -2270,7 +2328,7 @@ FormatPromptRecurse
calculate_format_addr_function_offset = true;
}
}
- else if (::strncmp (var_name_begin, "pc-offset}", strlen("pc-offset}")) == 0)
+ else if (IsToken (var_name_begin, "pc-offset}"))
{
StackFrame *frame = exe_ctx->GetFramePtr();
var_success = frame != NULL;
@@ -2285,33 +2343,33 @@ FormatPromptRecurse
break;
case 'l':
- if (::strncmp (var_name_begin, "line.", strlen("line.")) == 0)
+ if (IsToken (var_name_begin, "line."))
{
if (sc && sc->line_entry.IsValid())
{
var_name_begin += ::strlen ("line.");
- if (::strncmp (var_name_begin, "file.", strlen("file.")) == 0)
+ if (IsToken (var_name_begin, "file."))
{
var_name_begin += ::strlen ("file.");
- if (::strncmp (var_name_begin, "basename}", strlen("basename}")) == 0)
+ if (IsToken (var_name_begin, "basename}"))
{
format_file_spec.GetFilename() = sc->line_entry.file.GetFilename();
var_success = format_file_spec;
}
- else if (::strncmp (var_name_begin, "fullpath}", strlen("fullpath}")) == 0)
+ else if (IsToken (var_name_begin, "fullpath}"))
{
format_file_spec = sc->line_entry.file;
var_success = format_file_spec;
}
}
- else if (::strncmp (var_name_begin, "number}", strlen("number}")) == 0)
+ else if (IsTokenWithFormat (var_name_begin, "number", token_format, "%" PRIu64, exe_ctx, sc))
{
var_success = true;
- s.Printf("%u", sc->line_entry.line);
+ s.Printf(token_format.c_str(), (uint64_t)sc->line_entry.line);
}
- else if ((::strncmp (var_name_begin, "start-addr}", strlen("start-addr}")) == 0) ||
- (::strncmp (var_name_begin, "end-addr}", strlen("end-addr}")) == 0))
+ else if ((IsToken (var_name_begin, "start-addr}")) ||
+ (IsToken (var_name_begin, "end-addr}")))
{
var_success = sc && sc->line_entry.range.GetBaseAddress().IsValid();
if (var_success)
diff --git a/source/Interpreter/ScriptInterpreterPython.cpp b/source/Interpreter/ScriptInterpreterPython.cpp
index 684149726..9d9b8d93f 100644
--- a/source/Interpreter/ScriptInterpreterPython.cpp
+++ b/source/Interpreter/ScriptInterpreterPython.cpp
@@ -2685,7 +2685,7 @@ ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function,
error.SetErrorString("no function to execute");
return false;
}
- if (!g_swig_run_script_keyword_thread)
+ if (!g_swig_run_script_keyword_target)
{
error.SetErrorString("internal helper function missing");
return false;
@@ -2717,7 +2717,7 @@ ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function,
error.SetErrorString("no function to execute");
return false;
}
- if (!g_swig_run_script_keyword_thread)
+ if (!g_swig_run_script_keyword_frame)
{
error.SetErrorString("internal helper function missing");
return false;