blob: c2c44189dbe9b9a3162117b0d64bce56920add48 [file] [log] [blame]
/*
* 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.
*/
/**
* @author Sergey L. Ivashin
*/
#include "VMInterface.h"
#include "PMF.h"
#include "PMFAction.h"
#include "FixFileName.h"
#include "JITInstanceContext.h"
#include "CompilationContext.h"
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iomanip>
#ifdef _WIN32
#pragma pack(push)
#include <windows.h>
#pragma pack(pop)
#include <direct.h>
#define vsnprintf _vsnprintf
#define PATHSEPCHAR '\\'
#else //PLATFORM_POSIX
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/types.h>
#define PATHSEPCHAR '/'
#endif //ifdef _WIN32
using namespace std;
namespace Jitrino
{
//------ helper functions ---------------------------------------------------//
void create_dir (const char* dirname)
{
#ifdef _WIN32
int err;
if (_mkdir(dirname) != 0 && (err = errno) != 17)
cerr << "mkdir errno#" << err << " for <" << dirname << ">" << endl;
#else
int err;
if (mkdir(dirname, 0777 /*rwxrwxrwx*/) != 0 && (err = errno) != 17)
cerr << "mkdir errno#" << err << " for <" << dirname << ">" << endl;
#endif
}
void mkdir (char* fname)
{
for (char* sptr = fname; *sptr != 0; ++sptr)
if (*sptr == PATHSEPCHAR)
{
*sptr = 0;
if (*fname != 0)
create_dir(fname);
*sptr = PATHSEPCHAR;
}
}
static const char* c_str (Str& str)
{
if (str.count != 0)
{
const_cast<char*>(str.ptr)[str.count] = 0;
return str.ptr;
}
else
return "";
}
static const char* c_str (MemoryManager& mm, Str& str)
{
if (str.count != 0)
{
char* buffp = new (mm) char[str.count + 1];
memcpy(buffp, str.ptr, str.count);
buffp[str.count] = 0;
return buffp;
}
return "";
}
static const char* c_str (MemoryManager& mm, Str* fqnamep, size_t fqnsize)
{
size_t count = 0;
Str* strp = fqnamep;
for (size_t n = fqnsize; n != 0; --n, ++strp)
count += strp->count + 1;
if (count != 0)
{
char* buffp;
char* ptr = buffp = new (mm) char[count+1];
strp = fqnamep;
for (size_t n = fqnsize; n != 0; --n, ++strp)
{
const Str& str = *strp;
memcpy(ptr, str.ptr, str.count);
ptr += str.count;
*ptr++ = '.';
}
--ptr; // skip the last '.'
*ptr = 0;
return buffp;
}
return "";
}
static bool operator < (const Str& a, const Str& b)
{
return (a.count == b.count) ? strncmp(a.ptr, b.ptr, a.count) < 0 : a.count < b.count;
}
static bool operator == (const Str& a, const Str& b)
{
return (a.count == b.count) ? strncmp(a.ptr, b.ptr, a.count) == 0 : false;
}
static bool operator == (const Str& a, const char* s)
{
if (s == 0 || *s == 0)
return a.count == 0;
else
return (a.count == strlen(s)) ? strncmp(a.ptr, s, a.count) == 0 : false;
}
static bool compare (const Str* p, const Str* q, size_t count)
{
for (; count != 0; --count, ++p, ++q)
if (!(*p == *q))
return false;
return true;
}
ostream& operator << (ostream& os, const Str& a)
{
return os.write(a.ptr, a.count);
}
static ostream& operator << (ostream& os, const Strs& fqn)
{
for (size_t n = 0; n != fqn.size(); ++n)
{
if (n != 0)
os << '.';
os << fqn[n];
}
return os;
}
//---------------------------------------------------------------------------//
bool getBool (const char* val, bool def)
{
if (val != NULL)
{
if (strcmp(val, "true") == 0 || strcmp(val, "yes") == 0 || strcmp(val, "on") == 0)
def = true;
else if (strcmp(val, "false") == 0 || strcmp(val, "no") == 0 || strcmp(val, "off") == 0)
def = false;
else
crash("PMF: invalid value for bool parameter '%s' (true|false or yes|no or on|off is expected)\n", val);
}
return def;
}
//------ Str implementation -------------------------------------------------//
bool Str::trim ()
{
// trim the left end
while (count != 0 && isspace(*ptr))
--count, ++ptr;
// trim the right end
while (count != 0 && isspace(ptr[count-1]))
--count;
return count != 0;
}
const char* Str::findNext (char x, const char* nxt) const
{
if (count != 0)
{
const char* end = ptr + count;
if (nxt == 0)
nxt = ptr;
for (; nxt < end; ++nxt)
if (*nxt == x)
return nxt;
}
return 0;
}
const char* Str::find (const char* what) const
{
if (count != 0)
{
size_t n = strlen(what);
assert(n != 0);
const char* end = ptr + count - n;
for (const char* nxt = ptr; nxt <= end; ++nxt)
if (strncmp(nxt, what, n) == 0)
return nxt;
}
return 0;
}
//------ StrTokenizer declaration and implementation ------------------------//
struct StrTokenizer
{
const char* ptr;
const char* end;
char sep;
Str token;
StrTokenizer (char c, Str& s) : ptr(s.ptr), end(s.ptr + s.count), sep(c) {}
StrTokenizer (char c, const char* p, const char* e) : ptr(p), end(e), sep(c) {}
bool next ();
};
bool StrTokenizer::next ()
{
if (ptr == end)
return false;
token.ptr = ptr;
while (ptr != end && *ptr != sep)
++ptr;
token.count = ptr - token.ptr;
if (ptr != end)
++ptr;
token.trim();
return true;
};
//------ StrSubstitution declaration and implementation ---------------------//
struct StrSubstitution
{
typedef void (StrSubstitution::*Replacer) ();
struct Replace
{
Str what;
Replacer replace;
};
static Replace replaces[];
// Parameters for replacements
Str jitname;
const char* classname,
* methodname,
* signature;
int threadnb,
seqnb;
Str streamname;
// Some results of replecement
bool failure,
jit_specific,
thread_specific;
size_t rescount;
char result[MAXLOGFILENAME];
char* dptr,
* dend;
bool substitute (const Str& from);
void replace_jit ()
{
insert(jitname, false);
jit_specific = true;
}
void replace_class ()
{
if (classname) insert(Str(classname), true);
}
void replace_tree ()
{
if (classname) insert(Str(classname), false);
}
void replace_method ()
{
if (methodname) insert(Str(methodname), true);
if (signature) insert(Str(signature), true);
}
void replace_seqnb ()
{
dptr += sprintf(dptr, "%.6u", seqnb);
}
void replace_log ()
{
insert(Str(streamname), false);
}
void replace_thread ()
{
dptr += sprintf(dptr, "%u", threadnb);
thread_specific = true;
}
void insert (Str src, bool fix);
void seekz ();
};
StrSubstitution::Replace StrSubstitution::replaces[] =
{
{Str("%jit%"), &StrSubstitution::replace_jit},
{Str("%class%"), &StrSubstitution::replace_class},
{Str("%class_tree%"), &StrSubstitution::replace_tree},
{Str("%method%"), &StrSubstitution::replace_method},
{Str("%seqnb%"), &StrSubstitution::replace_seqnb},
{Str("%log%"), &StrSubstitution::replace_log},
{Str("%thread%"), &StrSubstitution::replace_thread}
};
bool StrSubstitution::substitute (const Str& from)
{
failure = jit_specific = thread_specific = false;
const char* sptr = from.ptr,
* send = from.ptr + from.count;
dptr = result,
dend = result + sizeof(result);
while (sptr != send)
{
bool replace = false;
size_t n;
Replace* rpend = replaces + sizeof(replaces)/sizeof(Replace);
for (Replace* rp = replaces; rp != rpend ; ++rp)
if (sptr + (n = rp->what.count) <= send &&
strncmp(sptr, rp->what.ptr, n) == 0)
{
(this->*rp->replace)();
sptr += n;
replace = true;
break;
}
if (!replace)
*dptr++ = *sptr++;
}
*dptr++ = 0;
rescount = dptr - result;
return !failure;
};
void StrSubstitution::insert (Str s, bool fix)
{
bool truncate = !(dptr + s.count < dend);
int max_len = truncate ? dend - dptr - 1 : s.count;
assert(max_len > 0);
if (s.empty())
{
failure = true;
return;
}
if (fix)
{
for (const char* sptr = s.ptr, * send = s.ptr + max_len; sptr != send;)
{
char c = *sptr++;
if (c == '/')
c = '_';
*dptr++ = c;
}
}
else
{
memcpy(dptr, s.ptr, max_len);
dptr += max_len;
}
}
void StrSubstitution::seekz ()
{
while (dptr < dend && *dptr != 0);
++dptr;
}
//------ Known Streams ------------------------------------------------------//
static const char* deffmask = "log/%jit%/%class%/%method%/%log%.log";
static const char* ct_deffmask = "log/%jit%/%class%/%method%/ct.log";
static const char* dot_deffmask = "log/%jit%/%class%/%method%/dot/.dot";
struct KnownStream
{
LogStream::SID sid;
const char* streamname;
const char* deffmask;
};
static KnownStream knownstreams[] =
{
{LogStream::INFO, "info", "log/info.log"},
{LogStream::RT, "rt", "log/rt.log"},
{LogStream::CT, "ct", ct_deffmask},
{LogStream::IRDUMP, "irdump", ct_deffmask},
{LogStream::DOTDUMP, "dotdump", dot_deffmask},
{LogStream::DBG, "dbg", deffmask}
};
const size_t nb_knownstreams = sizeof(knownstreams)/sizeof(KnownStream);
static KnownStream* isKnownStream (const Str& streamname)
{
for (size_t i = 0; i != nb_knownstreams; ++i)
if (streamname == knownstreams[i].streamname)
return &knownstreams[i];
return 0;
}
//------ LogStream implementation -------------------------------------------//
/*static*/ bool LogStream::ison = false;
/*static*/ LogStream LogStream::logsink;
/*static*/ LogStream* LogStream::logrtp = &LogStream::logsink;
LogStream::LogStream ()
:fname(0), enabled(false), append(false), pending_open(false), was_open(false), mutexp(0), ref_count(0)
{
}
LogStream::LogStream (/*const*/ char* s)
:fname(s), enabled(true), append(false), pending_open(true), was_open(false), mutexp(0), ref_count(0)
{
}
LogStream::~LogStream ()
{
close();
}
void LogStream::open (/*const*/ char* s)
{
assert(!was_open && !pending_open);
fname = s;
enabled = true;
pending_open = true;
}
void LogStream::close ()
{
if (was_open)
{
was_open = false;
os.close();
}
}
#ifndef _NOLOG
int LogStream::printf (const char* fmt, ...)
{
AutoUnlock lock(mutexp);
if (lazy_open())
{
va_list args;
va_start(args, fmt);
char buff[256];
int n = vsnprintf(buff, sizeof(buff), fmt, args);
os << buff;
return n;
}
else
return 0;
}
#endif
bool LogStream::lazy_open ()
{
if (pending_open)
{
pending_open = false;
ios_base::openmode mode = ios_base::out;
if (append)
mode |= ios_base::app;
char temp[MAXFILENAMESIZE];
fix_file_name(temp, sizeof(temp), fname);
mkdir(temp);
os.open(temp, mode);
was_open = os.is_open();
if (!was_open)
cerr << "Failed to open log file '" << temp << "'" << endl;
}
return was_open;
}
//TODO: Is it possible to remove locking there?
void LogStream::addRef ()
{
AutoUnlock lock(mutexp);
++ref_count;
}
bool LogStream::releaseRef ()
{
AutoUnlock lock(mutexp);
if (--ref_count == 0)
close();
assert(ref_count >= 0);
return ref_count == 0;
}
LogStream& LogStream::log (LogStream::SID sid, const char* pipename)
{
return log(sid, pipename, pipename == 0 ? 0 : strlen(pipename));
}
LogStream& LogStream::log (LogStream::SID sid, const char* pipename, size_t namesz)
{
if (isOn())
{
Str pipe(pipename, namesz);
LogStreams& streams = LogStreams::current();
LogStream* lsp = 0;
for (size_t idx = 0, n = streams.size(); idx != n; ++idx)
{
const LogTemplate& lt = streams.logtemplate(idx);
if ((lt.pathp == 0 || lt.pathp->empty()) && lt.sid == sid) {
if (lt.filtername == pipe)
return streams.logstream(idx);
else if (lt.filtername.empty())
lsp = &streams.logstream(idx);
}
}
if (lsp != 0)
return *lsp;
}
return log_sink();
}
LogStream& LogStream::log (const char* streamname, const char* pipename)
{
return log(streamname, pipename, pipename == 0 ? 0 : strlen(pipename));
}
LogStream& LogStream::log (const char* streamname, const char* pipename, size_t namesz)
{
if (isOn())
{
assert(streamname != 0 && *streamname != 0);
Str pipe(pipename, namesz);
LogStreams& streams = LogStreams::current();
LogStream* lsp = 0;
for (size_t idx = 0, n = streams.size(); idx != n; ++idx)
{
const LogTemplate& lt = streams.logtemplate(idx);
if ((lt.pathp == 0 || lt.pathp->empty()) && lt.streamname == streamname) {
if (lt.filtername == pipe)
return streams.logstream(idx);
else if (lt.filtername.empty())
lsp = &streams.logstream(idx);
}
}
if (lsp != 0)
return *lsp;
}
return log_sink();
}
//------ LogStreams implementation ------------------------------------------//
LogStreams::LogStreams (MemoryManager& mm_, PMF& pmf_, int t)
:mm(mm_), pmf(pmf_), nbos(pmf.getLogTemplates().size())
, streams(mm, nbos), streamsstack(mm, nbos)
, depth(0), threadnb(t), methodnb(0)
{
StrSubstitution ss;
ss.jitname = pmf.jitname;
ss.classname = 0;
ss.methodname = 0;
ss.signature = 0;
ss.threadnb = threadnb;
ss.seqnb = methodnb;
for (size_t sx = 0; sx != nbos; ++sx)
{
streams[sx] = &LogStream::logsink;
const LogTemplate& ltemplate = pmf.getLogTemplates()[sx];
if (ltemplate.enabled)
{
streamsstack[sx] = new (mm) Streams(mm);
ss.streamname = ltemplate.streamname;
if (ss.substitute(ltemplate.fmask))
{// File name doesn't require compilation context (class or method name)
AutoUnlock lock(*pmf.pfiles);
assign(sx, ss.result, ss.rescount);
}
}
// Turn on the whole logging if there are log templates defined
LogStream::ison = true;
}
}
LogStreams::~LogStreams ()
{
LogStream* lsp;
for (size_t sx = 0; sx != nbos; ++sx)
if ((lsp = streams[sx])->enabled)
{
AutoUnlock lock(*pmf.pfiles);
if (lsp->releaseRef())
(*pmf.pfiles)[lsp->fname] = 0;
#ifdef _DEBUG_PMF_STREAMS
cout << "LogStreams[" << threadnb << "] depth:" << depth << " idx:" << sx
<< " free '" << lsp->fname
<< "' closed:" << ((*pmf.pfiles)[lsp->fname] == 0)
<< endl;
#endif
}
}
void LogStreams::beginMethod (const char* cname,
const char* mname,
const char* sig,
int nb)
{
++depth;
StrSubstitution ss;
ss.jitname = pmf.jitname;
ss.classname = cname;
ss.methodname = mname;
ss.signature = sig;
ss.threadnb = threadnb;
ss.seqnb = nb;
for (size_t sx = 0; sx != nbos; ++sx)
{
const LogTemplate& ltemplate = pmf.getLogTemplates()[sx];
if (ltemplate.enabled)
{
LogStream* lsp = streams[sx];
Streams* sstack = streamsstack[sx];
ss.streamname = ltemplate.streamname;
ss.substitute(ltemplate.fmask);
if (!lsp->enabled || strcmp(lsp->fname, ss.result) != 0)
{// File name changed: stream will be changed, so save stream on stack
sstack->push_back(lsp);
#ifdef _DEBUG_PMF_STREAMS
cout << "LogStreams[" << threadnb << "] depth:" << depth << " idx:" << sx
<< "pushed '" << (lsp->enabled ? lsp->fname : "-none-")
<< "'"
<< endl;
#endif
// change stream - use new or reuse already open stream
AutoUnlock lock(*pmf.pfiles);
assign(sx, ss.result, ss.rescount);
}
else
{// File name didn't changed, continue the current stream
sstack->push_back(0);
}
}
}
}
void LogStreams::endMethod ()
{
assert(depth > 0);
for (size_t sx = 0; sx != nbos; ++sx)
{
LogStream* lsp = streams[sx];
if (lsp->enabled)
{
Streams* sstack = streamsstack[sx];
if (sstack->back() != 0)
{// Return to the saved stream
AutoUnlock lock(*pmf.pfiles);
// If this file no longer needed, close it but remember its name.
// Next time the file will be open, it will be open in append mode.
if (lsp->releaseRef())
(*pmf.pfiles)[lsp->fname] = 0;
#ifdef _DEBUG_PMF_STREAMS
cout << "LogStreams[" << threadnb << "] depth:" << depth << " idx:" << sx
<< " free '" << lsp->fname
<< "' closed:" << ((*pmf.pfiles)[lsp->fname] == 0)
<< endl;
#endif
streams[sx] = sstack->back();
#ifdef _DEBUG_PMF_STREAMS
cout << "LogStreams[" << threadnb << "] depth:" << depth << " idx:" << sx
<< " poped '" << (streams[sx]->enabled ? streams[sx]->fname : "-none-")
<< "'"
<< endl;
#endif
}
else
{// Stream didn't changed
}
sstack->pop_back();
}
}
--depth;
}
void LogStreams::assign (size_t sx, const char* fname, size_t fnamesz)
{
const LogTemplate& ltemplate = pmf.getLogTemplates()[sx];
LogStream* lsp;
PMF::Files::iterator ptr = pmf.pfiles->find(fname);
if (ptr == pmf.pfiles->end() || ptr->second == 0)
{// There is no file open with this name.
lsp = new (pmf.mm) LogStream();
// If %jit% macros used in the file name template, then this file will
// not be shared by several thread. Otherwise, synchronization is
// neccessary.
if (!ltemplate.thread_specific)
lsp->mutexp = new (mm) Mutex();
lsp->append = ltemplate.append;
if (!ltemplate.append && ptr != pmf.pfiles->end())
lsp->append = true; // the file was opened before
lsp->enabled = lsp->pending_open = true;
lsp->fname = new (mm) char[fnamesz];
memcpy(lsp->fname, fname, fnamesz);
(*pmf.pfiles)[lsp->fname] = lsp;
// Special case of rt stream - only the single instance of LogStream object can exist
if (ltemplate.sid == LogStream::RT)
LogStream::logrtp = lsp;
#ifdef _DEBUG_PMF_STREAMS
cout << "LogStreams[" << threadnb << "] depth:" << depth << " idx:" << sx
<< " new '" << lsp->fname
<< "' seen before:" << (ptr != pmf.pfiles->end())
<< endl;
#endif
}
else
{
lsp = ptr->second;
#ifdef _DEBUG_PMF_STREAMS
cout << "LogStreams[" << threadnb << "] depth:" << depth << " idx:" << sx
<< " reuse '" << lsp->fname
<< "'"
<< endl;
#endif
}
lsp->addRef();
streams[sx] = lsp;
}
const LogTemplate& LogStreams::logtemplate (size_t idx) const
{
return pmf.logtemplates.at(idx);
}
//------ logDisplay implementation ------------------------------------------//
LogDisplay::LogDisplay (MemoryManager& mm)
:streamidxs(mm, nb_knownstreams)
{
for (size_t sid = 0; sid != nb_knownstreams; ++sid)
streamidxs[sid] = 0; // index of sink stream
}
void LogDisplay::add (size_t idx, LogTemplate& lt)
{
KnownStream* ksp = isKnownStream(lt.streamname);
if (ksp != 0)
streamidxs.at(ksp->sid) = idx;
else
streamidxs.push_back(idx);
}
LogStream& LogDisplay::log (LogStream::SID sid) const
{
size_t idx = streamidxs.at(sid);
return LogStreams::current().logstream(idx);
}
//------ PMF::Cmd declaration and implementation ----------------------------//
struct PMF::Cmd
{
char* buff; // own copy of the command source
Strs* left; // left part (before '=' character)
Str right; // right part (after '=' character)
bool arg, // true for 'arg' type command
log; // true for 'log' type command
Str jitname, // jit name string (left[0] in fact)
filtername; // filter name string
int xkeyword, // index of keyword found in 'left' array
xlog; // for 'log' type command only - index of 'log' keyword
Cmd () : buff(0), arg(false), log(false) {}
int strength (size_t pathsz = 0) const;
void fatal (const char* msg) const;
};
static int cmd_strength (bool jit_known, bool filter_known, size_t path_size)
{
return int( ((1 + (filter_known ? 2 : 0) + (jit_known ? 4 : 0)) << 16) + path_size );
}
int PMF::Cmd::strength (size_t pathsz) const
{
return cmd_strength(!jitname.empty(), !filtername.empty(), pathsz);
}
void PMF::Cmd::fatal (const char* msg) const
{
crash("Error in command line '%s'\n%s\n", left->front().ptr, msg);
}
//------ PMF::MethodFilter implementation -----------------------------------//
// Parse filter specification with the following syntax:
// <class name> . <method name> <signature>
// It is assumed that <signature> starts with the '(' character.
//
void PMF::MethodFilter::init (const Str& filter)
{
const char* ptr1 = filter.findFirst('.');
if (ptr1 == 0)
ptr1 = filter.findFirst(':');
const char* ptr2 = filter.findNext('(', ptr1);
const char* ptr3 = filter.end();
if (ptr1 == 0)
// no class name
classname.count = 0,
methodname.ptr = filter.ptr;
else
{// classname, then method name
classname.ptr = filter.ptr,
classname.count = ptr1 - filter.ptr,
++ptr1; // skip the '.'/':' character
if (ptr1 < filter.end() && *ptr1 == ':')
++ptr1; // skip the second ':'
methodname.ptr = ptr1;
}
if (ptr2 == 0)
// no method signature
methodname.count = ptr3 - methodname.ptr,
signature.count = 0;
else
methodname.count = ptr2 - methodname.ptr,
signature.ptr = ptr2, // include the '(' character
signature.count = ptr3 - ptr2;
classname.trim(),
methodname.trim(),
signature.trim();
if (classname.empty() && methodname.findFirst('/') != 0)
classname = methodname,
methodname.clear();
}
bool PMF::MethodFilter::empty () const
{
return classname.empty() &&
methodname.empty() &&
signature.empty();
}
int PMF::MethodFilter::pass (const char* cname, const char* mname, const char* sig) const
{
if (cname != 0 && strncmp(cname, classname.ptr, classname.count) != 0)
return -1;
if (mname != 0 && strncmp(mname, methodname.ptr, methodname.count) != 0)
return -1;
if (sig != 0 && strncmp(sig, signature.ptr, signature.count) != 0)
return -1;
return (int)(classname.count + methodname.count + signature.count);
}
const char* PMF::MethodFilter::c_str (MemoryManager& mm) const
{
size_t count = classname.count + 1 + methodname.count + signature.count;
char* cstr = new (mm) char[count+1];
char* ptr = cstr;
memcpy(ptr, classname.ptr, classname.count);
ptr += classname.count;
*ptr++ = '.';
memcpy(ptr, methodname.ptr, methodname.count);
ptr += methodname.count;
memcpy(ptr, signature.ptr, signature.count);
ptr += signature.count;
*ptr = 0;
return cstr;
}
//------ PMF::Args implementation -------------------------------------------//
void PMF::Args::add (const char* key, const char* value, int strength, Cmd* cmdp)
{
Store::iterator end = store.end(),
ptr = find(store.begin(), end, key);
if (ptr == end)
{
Arg arg;
arg.key = key,
arg.value = value;
arg.strength = strength;
arg.cmdp = cmdp;
store.push_back(arg);
}
else if (strength >= ptr->strength)
{
ptr->value = value,
ptr->strength = strength;
ptr->cmdp = cmdp;
}
}
const char* PMF::Args::get (const char* key) const
{
Store::const_iterator end = store.end(),
ptr = find(store.begin(), end, key);
if (ptr == end)
return 0;
else
return ptr->value;
}
//------ Alias declaration and implementation ------------------------------//
struct PMF::Pipeline::Alias
{
Str name;
struct Child
{
Str name;
enum Type {REQUIRED, DEFON, DEFOFF} type;
Alias* aliasp;
bool operator == (const Str& s) const {return name == s;}
};
typedef StlVector<Child> Childs;
Childs childs;
Alias (MemoryManager& mm) : childs(mm) {}
Alias (MemoryManager& mm, const Alias&);
};
// shallow copy
//
PMF::Pipeline::Alias::Alias (MemoryManager& mm, const Alias& orig)
:childs(mm)
{
name = orig.name;
childs.insert(childs.end(), orig.childs.begin(), orig.childs.end());
for (Childs::iterator it = childs.begin(); it != childs.end(); ++it)
it->aliasp = 0;
}
//------ ArgIterator declaration and implementation ------------------------//
struct PMF::ArgIterator
{
const Pipeline::Step& step;
const char* key;
Cmds::iterator it;
bool first;
ArgIterator (const Pipeline::Step& s, const char* k) :step(s), key(k), first(true) {}
bool next ();
operator Cmd* () {return *it;}
};
bool PMF::ArgIterator::next ()
{
Cmds::iterator end = step.pipeline->pmf.cmds.end();
for (;;)
{
if (first)
{
first = false;
it = step.pipeline->pmf.cmds.begin();
}
else
{
if (it == end)
return false;
++it;
}
if (it == end)
return false;
if (*it == 0 || !(*it)->arg)
continue;
Cmd& cmd = **it;
if (!cmd.filtername.empty() && !(cmd.filtername == step.pipeline->name))
continue;
// index of argument
size_t xend = cmd.left->size() - 1;
// size of path in command (i.e. number of items between argument and keuword)
size_t pathsz = xend - cmd.xkeyword - 1;
if (pathsz > step.fqname->size())
continue;
if (key != 0 && !(cmd.left->at(xend) == key))
continue;
if (pathsz == 1 && cmd.left->at(cmd.xkeyword + 1) == step.factory->getName())
return true;
size_t xl, xs;
for (xl = cmd.xkeyword + 1, xs = 0; xl != xend; ++xl, ++xs)
if (!(cmd.left->at(xl) == step.fqname->at(xs)))
break;
if (xl == xend)
return true;
}
}
//------ PMF implementation ------------------------------------------------//
bool PMF::parse (Cmd& cmd, const char* keyword, FilterSpecs& filterspecs)
{
Strs::iterator i = find(cmd.left->begin(), cmd.left->end(), keyword);
if (i == cmd.left->end())
return false;
cmd.xkeyword = (int)(i - cmd.left->begin());
Str* tmp;
if (cmd.xkeyword == 0)
{// <keyword> ....
// no jit name
// no filter name
// obsolete form of filter command:
// filter.a = ...
if (strcmp(keyword, "filter") == 0)
{
if (cmd.left->size() == 2 && !(tmp = &cmd.left->at(1))->empty())
cmd.filtername = *tmp;
}
}
else if (cmd.xkeyword == 1 && !(tmp = &cmd.left->at(0))->empty())
{// <a>.<keyword> ...
// <a> can be name of jit name or filter name
if (strcmp(keyword, "filter") == 0 || filterspecs.find(*tmp) != filterspecs.end())
cmd.filtername = *tmp;
else
cmd.jitname = *tmp;
}
else if (cmd.xkeyword == 2)
{// <a>.<b>.<keyword> ...
// <a> must be jit name
// <b> must be filter name
if (!(tmp = &cmd.left->at(0))->empty())
cmd.jitname = *tmp;
if (!(tmp = &cmd.left->at(1))->empty())
{
cmd.filtername = *tmp;
}
}
else
{
cmd.fatal("Keyword misplaced");
}
return cmd.jitname.empty() || cmd.jitname == jitname;
}
void PMF::processCmd (Cmd& cmd)
{
// Split line into tokens <left0> . <left1> . .... = <right>
const char* ptr0 = cmd.buff ;
const char* ptr1 = ptr0;
while (*ptr1 != '=' && *ptr1 != 0)
++ptr1;
for (StrTokenizer tokens('.', ptr0, ptr1); tokens.next();)
cmd.left->push_back(tokens.token);
if (cmd.left->empty())
crash("Empty left part of command line '%s'", ptr0);
if (*ptr1 == 0)
cmd.right.clear();
else
{
cmd.right.init(ptr1+1);
cmd.right.trim();
}
// Special processing for read command
if (cmd.left->size() == 1 && cmd.left->at(0) == "read")
{
if (cmd.right.empty())
cmd.fatal("File name missing in read command");
ifstream is(c_str(mm, cmd.right));
if (is.fail())
cmd.fatal("File not found");
char buff[1024];
while (is.good())
{
is.getline(buff, sizeof(buff));
processCmd(buff);
}
is.close();
}
// Special processing for help command
else if (strncmp(cmd.buff, "help", 4) == 0 ||
strncmp(cmd.buff, "arg.help", 8) == 0)
{
help_requested = cmd.right;
}
// Any other command lines are simply stored
else
cmds.push_back(&cmd);
}
void PMF::processCmd (const char* key, const char* value)
{
// Must be called before init()
assert(!initialized);
assert(key != 0);
if (*key == 0 || value == 0)
return;
// Copy to internal buffer
size_t count1 = strlen(key),
count2 = strlen(value);
Cmd& cmd = *new (mm) Cmd();
cmd.buff = new (mm) char[count1 + 1 + count2 + 1];
cmd.left = new (mm) Strs(mm);
memcpy(cmd.buff, key, count1);
cmd.buff[count1] = '=';
memcpy(cmd.buff+count1+1, value, count2+1);
processCmd(cmd);
}
void PMF::processCmd (const char* ptr)
{
// Must be called before init()
assert(!initialized);
// Ignore empty and comment command lines
while (isspace(*ptr))
++ptr;
size_t count = strlen(ptr);
if (count == 0)
return; // skip empty command
if (ptr[0] == '#')
return; // skip #comment
if (count > 1 && ptr[0] == '/' && ptr[1] == '/')
return; // skip //comment
// Copy to internal buffer
Cmd& cmd = *new (mm) Cmd();
cmd.buff = new (mm) char[count+1];
cmd.left = new (mm) Strs(mm);
memcpy(cmd.buff, ptr, count+1);
processCmd(cmd);
}
void PMF::processVMProperties ()
{
for (VMPropertyIterator it(mm, "jit."); it.next();) {
processCmd(it.getKey() + 4, it.getValue());
}
}
PMF::Files* PMF::pfiles = 0;
PMF::PMF (MemoryManager& m, JITInstanceContext& jit)
:initialized(false),mm(m), jitInstanceContext(jit)
,cmds(mm), logtemplates(mm), pipelines(mm)
{
jitname.init(jitInstanceContext.getJITName().c_str());
if (pfiles == 0)
pfiles = new (mm) Files(mm);
}
void PMF::init (bool first_)
{
assert(!initialized);
first = first_;
help_requested = false;
processVMProperties();
initialized = true;
#ifdef _DEBUG_PMF
cout << endl << "Commands received:" << endl;
for (Cmds::iterator it = cmds.begin(); it != cmds.end(); ++it)
{
Cmd& cmd = **it;
cout << *cmd.left << " = <" << cmd.right << "> " << endl;
}
cout << endl << "ActionFactory:" << endl;
for (IActionFactory* afp = IActionFactory::getFirst(); afp != 0; afp = afp->getNext())
cout << " " << afp->getName() << endl;
#endif
if (first && !help_requested.empty() && !(help_requested == "jit"))
showHelp(cout);
// Create common pipeline with empty filter
pipelines.push_back(new (mm) Pipeline(*this));
// Process all filter statements
FilterSpecs filterspecs(mm);
Cmd* cmdp;
for (Cmds::iterator it = cmds.begin(); it != cmds.end(); ++it)
if ((cmdp = *it) != 0 && parse(*cmdp, "filter", filterspecs))
{
*it = 0;
if (cmdp->right.empty())
cmdp->fatal("Empty filter");
if (cmdp->filtername.empty())
cmdp->fatal("Invalid filter name");
if (cmdp->filtername == jitname)
cmdp->fatal("Invalid filter name - the same as jit name");
if (filterspecs.find(cmdp->filtername) != filterspecs.end())
cmdp->fatal("Duplicate filters defined");
filterspecs[cmdp->filtername] = &cmdp->right;
}
// Process all path statements
for (Cmds::iterator it = cmds.begin(); it != cmds.end(); ++it)
if ((cmdp = *it) != 0 && parse(*cmdp, "path", filterspecs))
{
*it = 0;
if (!cmdp->filtername.empty() && filterspecs.find(cmdp->filtername) == filterspecs.end())
continue;
// Process left of '=" part
Str* namep = 0;
size_t x = cmdp->left->size() - cmdp->xkeyword - 1;
if (x == 0)
{
}
else if (x == 1)
{
Str& name = cmdp->left->at(cmdp->xkeyword + 1);
if (!name.empty())
namep = &name;
}
else
cmdp->fatal("Extra items after path name");
Pipeline* pipeline = lookup(&cmdp->filtername, true);
Pipeline::Alias* aliasp = pipeline->lookup(namep);
if (aliasp == 0)
{
aliasp = new (mm) Pipeline::Alias(mm);
if (namep == 0)
pipeline->root = aliasp;
else
aliasp->name = *namep;
pipeline->aliases->push_back(aliasp);
}
else
{
cmdp->fatal("Multiple path defined");
}
// Process right of '=" part
for (StrTokenizer tokens(',', cmdp->right); tokens.next();)
{
Pipeline::Alias::Child child;
child.aliasp = 0;
child.type = Pipeline::Alias::Child::DEFON;
if (!tokens.token.empty())
{
char x = tokens.token.ptr[tokens.token.count-1];
if (x == '!')
{
child.type = Pipeline::Alias::Child::REQUIRED;
--tokens.token.count;
}
else if (x == '+')
{
child.type = Pipeline::Alias::Child::DEFON;
--tokens.token.count;
}
else if (x == '-')
{
child.type = Pipeline::Alias::Child::DEFOFF;
--tokens.token.count;
}
tokens.token.trim();
}
if (tokens.token.empty())
cmdp->fatal("Empty path child name");
child.name = tokens.token;
aliasp->childs.push_back(child);
}
}
else if (cmdp != 0 && !cmdp->jitname.empty() && !(cmdp->jitname == jitname))
*it = 0; // not for this jit command
// Process all arg statements
for (Cmds::iterator it = cmds.begin(); it != cmds.end(); ++it)
if ((cmdp = *it) != 0 && parse(*cmdp, "arg", filterspecs))
{
cmdp->arg = true;
lookup(&cmdp->filtername, true);
}
else if (cmdp != 0 && !cmdp->jitname.empty() && !(cmdp->jitname == jitname))
*it = 0; // not for this jit command
// Path resolution
Pipeline& compipeline = **pipelines.begin(); // this is the common (empty) pipeline
if (compipeline.root == 0)
compipeline.stop("PMF: Invalid common pipeline for '%s' - does not have a root path\n");
for (Pipelines::iterator k = pipelines.begin(); k != pipelines.end(); ++k)
{
Pipeline& pipeline = **k;
Pipeline::Aliases& aliases = *pipeline.aliases;
if (&pipeline != &compipeline)
{
FilterSpecs::iterator it = filterspecs.find(pipeline.name);
if (it == filterspecs.end())
pipeline.stop("PMF: Invalid pipeline for '%s.%s' - unspecified filter\n");
pipeline.method.init(*it->second);
if (pipeline.method.empty())
pipeline.stop("PMF: Invalid pipeline for '%s.%s' - empty filter\n");
}
if (pipeline.root == 0)
{
pipeline.root = new (mm) Pipeline::Alias(mm, *compipeline.root);
aliases.push_back(pipeline.root);
}
for (;;)
{
Pipeline::Aliases newaliases(mm);
for (Pipeline::Aliases::iterator l = aliases.begin(); l != aliases.end(); ++l)
{
Pipeline::Alias::Childs::iterator end = (*l)->childs.end(),
ptr = (*l)->childs.begin();
for (; ptr != end; ++ptr)
{
ptr->aliasp = pipeline.lookup(&ptr->name);
if (ptr->aliasp == 0 && &pipeline != &compipeline)
{
Pipeline::Alias* comaliasp;
if ((comaliasp = compipeline.lookup(&ptr->name)) != 0)
newaliases.push_back(new (mm) Pipeline::Alias(mm, *comaliasp));
}
}
}
if (newaliases.empty())
break;
else
aliases.insert(aliases.end(), newaliases.begin(), newaliases.end());
}
Strs fqname(mm);
walk(pipeline, pipeline.root, fqname);
}
// Build parameters map
for (Cmds::iterator it = cmds.begin(); it != cmds.end(); ++it)
if ((cmdp = *it) != 0 && cmdp->arg)
{
size_t n = cmdp->left->size() - cmdp->xkeyword - 1;
if (n == 0)
cmdp->fatal("No argument name");
}
initStreams();
for (Pipelines::iterator k = pipelines.begin(); k != pipelines.end(); ++k)
{
Pipeline& pipeline = **k;
for (Pipeline::Steps::iterator s = pipeline.steps->begin(); s != pipeline.steps->end(); ++s)
{
Pipeline::Step& step = *s;
step.setup(mm);
}
}
// Create actions for common filter
for (Pipeline::Steps::iterator s = compipeline.steps->begin(); s != compipeline.steps->end(); ++s)
{
Pipeline::Step& step = *s;
step.action = step.factory->createAction(mm);
step.reused = false;
}
// Create actions for other filters
for (Pipelines::iterator k = pipelines.begin() + 1; k != pipelines.end(); ++k)
{
Pipeline& pipeline = **k;
for (Pipeline::Steps::iterator s = pipeline.steps->begin(); s != pipeline.steps->end(); ++s)
{
Pipeline::Step& step = *s;
step.action = 0;
step.reused = false;
// Check if this step has any filter-specific agruments
bool filterspec = false;
if (step.args != 0)
for (Args::Store::iterator m = step.args->store.begin(); m != step.args->store.end(); ++m)
if (!m->cmdp->filtername.empty())
{
filterspec = true;
break;
}
// If step doesn't have such arguments, action from common filter can be reused
if (!filterspec)
for (Pipeline::Steps::iterator m = compipeline.steps->begin(); m != compipeline.steps->end(); ++m)
if (step.factory == m->factory)
{
step.action = m->action;
step.reused = true;
break;
}
// Otherwise action is created and initiated
if (step.action == 0)
step.action = step.factory->createAction(mm);
}
}
if (help_requested == "jit" || help_requested == "all")
showHelpJits(cout);
// Debug output
#ifdef _DEBUG_PMF
cout << endl << "PMF jit<" << jitname << ">" << endl;
for (Pipelines::iterator k = pipelines.begin(); k != pipelines.end(); ++k)
{
Pipeline& pipeline = **k;
cout << " Pipeline<" << pipeline.name <<
"> method<" << pipeline.method.classname
<< "><" << pipeline.method.methodname
<< "><" << pipeline.method.signature
<< ">" << endl;
for (Pipeline::Aliases::iterator l = pipeline.aliases->begin(); l != pipeline.aliases->end(); ++l)
{
Pipeline::Alias& alias = **l;
cout << " Alias<" << alias.name << ">";
if (pipeline.root == &alias)
cout << " *root*";
cout << endl;
for (Pipeline::Alias::Childs::iterator m = alias.childs.begin(); m != alias.childs.end(); ++m)
{
Pipeline::Alias::Child& child = *m;
cout << " Child<" << child.name << "> kind:" << child.type;
if (child.aliasp!= 0)
cout << " to <" << child.aliasp->name << ">";
cout << endl;
}
}
for (Pipeline::Steps::iterator l = pipeline.steps->begin(); l != pipeline.steps->end(); ++l)
{
Pipeline::Step& step = *l;
cout << " Step <" << *step.fqname
<< "> action <" << step.factory->getName()
<< "> reused:" << step.reused
<< endl;
if (step.args != 0)
for (Args::Store::iterator m = step.args->store.begin(); m != step.args->store.end(); ++m)
{
Args::Arg& arg = *m;
cout << " arg <" << arg.key << "> = <" << arg.value
<< "> strength:" << arg.strength
<< endl;
}
if (step.logs != 0)
for (size_t sid = 0; sid < step.logs->streamidxs.size(); ++sid)
{
size_t idx = step.logs->streamidxs.at(sid);
cout << " sid#" << sid
<< " idx#" << idx
<< endl;
}
}
}
#endif
// Initialize commom pipeline only (others get intialized as need by PipelineIterator)
compipeline.init();
}
void PMF::deinit ()
{
for (Pipelines::iterator it = pipelines.begin(); it != pipelines.end(); ++it)
(*it)->deinit();
}
void PMF::initStreams ()
{
// There are two forms of the log command:
//
// 1) stream enable
// [<jit>.][<filter>.]arg.[<path>.]log = <stream>, <stream>, ...
//
// 2) stream assignment
// [<jit>.][<filter>.]arg.[<path>.]log.<stream>.file = <file mask>
PMF::Cmd* cmdp;
for (PMF::Cmds::iterator it = cmds.begin(); it != cmds.end(); ++it)
if ((cmdp = *it) != 0 && cmdp->arg)
{
Strs::iterator leftbeg = cmdp->left->begin(),
leftend = cmdp->left->end();
Strs::iterator i = find(leftbeg + cmdp->xkeyword + 1, leftend, "log");
// log command must contain the "log" keyword
if (i == leftend)
continue;
cmdp->log = true;
cmdp->xlog = int(i - leftbeg);
int xpath = cmdp->xkeyword + 1,
xlast = (int)cmdp->left->size() - 1;
if (cmdp->xlog == xlast)
{// stream enable command
for (StrTokenizer tokens(',', cmdp->right); tokens.next();)
{
LogTemplate& lt = lookStream(tokens.token, cmdp, xpath, cmdp->xlog);
lt.enabled = true;
}
}
}
for (LogTemplates::iterator it = logtemplates.begin(); it != logtemplates.end(); ++it)
{
LogTemplate& lt = *it;
int file_strength = 0,
append_strength = 0;
for (PMF::Cmds::iterator kt = cmds.begin(); kt != cmds.end(); ++kt)
if ((cmdp = *kt) != 0 && cmdp->log)
{
if (!cmdp->filtername.empty() && !(cmdp->filtername == lt.filtername))
continue;
int xpath = cmdp->xkeyword + 1,
xlast = (int)cmdp->left->size() - 1,
pathsz = cmdp->xlog - xpath;
Str& key = cmdp->left->at(xlast);
if (key == "log")
continue;
// compare stream name
if (cmdp->xlog == xlast - 2)
if (!(cmdp->left->at(cmdp->xlog+1) == lt.streamname))
continue;
// compare path names
int ltpathsz = (lt.pathp == 0) ? 0 : (int)lt.pathp->size();
if (pathsz > ltpathsz)
continue;
if (pathsz != 0 && !compare(&cmdp->left->at(xpath), &lt.pathp->at(0), pathsz))
continue;
int strength = cmdp->strength(pathsz);
if (key == "file" && strength > file_strength)
{// stream assignment command
lt.fmask = cmdp->right;
file_strength = strength;
}
else if (key == "append" && strength > append_strength)
{
lt.append = (cmdp->right == "true" || cmdp->right == "yes");
file_strength = strength;
}
else
cmdp->fatal("Invalid log command");
}
}
// Find out if file name mask containds some key macros
for (LogTemplates::iterator it = logtemplates.begin(); it != logtemplates.end(); ++it)
{
LogTemplate& lt = *it;
lt.jit_specific = lt.fmask.find("%jit%") != 0;
lt.thread_specific = lt.fmask.find("%thread%") != 0;
}
#ifdef _DEBUG_PMF
cout << endl << "LogTemplates" << endl;
for (size_t idx = 0; idx != logtemplates.size(); ++idx)
{
LogTemplate& lt = logtemplates.at(idx);
cout << idx
<< ") stream <" << lt.streamname
<< "> enabled=" << lt.enabled
<< " append=" << lt.append
<< " jit_specific=" << lt.jit_specific
<< " thread_specific=" << lt.thread_specific
<< " filter <" << lt.filtername
<< "> path <";
if (lt.pathp != 0)
cout << *lt.pathp;
cout << "> fmask <" << lt.fmask
<< ">" << endl;
}
#endif
}
LogTemplate& PMF::lookStream (Str& streamname, PMF::Cmd* cmdp, size_t xpath, size_t xlog)
{
LogTemplates::iterator end = logtemplates.end(),
ptr = logtemplates.begin();
for (; ptr != end; ++ptr)
{
if (ptr->filtername == cmdp->filtername &&
ptr->streamname == streamname)
{
if (ptr->pathp->empty() && xpath != xlog)
continue;
if (!ptr->pathp->empty())
{
if (ptr->pathp->size() != xlog - xpath)
continue;
if (!compare(&ptr->pathp->at(0), &cmdp->left->at(xpath), xlog - xpath))
continue;
}
cmdp->fatal("Duplicate streams defined");
}
}
if (logtemplates.empty())
{// create the "sink' stream at index o
logtemplates.push_back(LogTemplate());
LogTemplate& lt = logtemplates.back();
lt.idx = 0;
lt.streamname.init("sink");
}
logtemplates.push_back(LogTemplate());
LogTemplate& lt = logtemplates.back();
lt.idx = logtemplates.size()-1;
lt.filtername = cmdp->filtername;
lt.streamname = streamname;
lt.pathp = new (mm) Strs(mm);
if (xpath != xlog)
{
Strs::iterator leftbeg = cmdp->left->begin();
lt.pathp->insert(lt.pathp->end(), leftbeg + xpath, leftbeg + xlog);
}
lt.fmask = deffmask;
lt.sid = static_cast<LogStream::SID>(nb_knownstreams);
KnownStream* ksp = isKnownStream(streamname);
if (ksp != 0)
{
lt.fmask = ksp->deffmask;
lt.sid = ksp->sid;
}
if (ksp != 0 && ksp->sid == LogStream::RT)
{
if (!lt.pathp->empty() || !lt.filtername.empty())
crash("'rt' stream must be defined at global level only\n");
}
return lt;
}
PMF::HPipeline PMF::selectPipeline (const char* cn, const char* mn, const char* sig) const
{
Pipeline* pipeline = *pipelines.begin();
int xs = 0, x;
if (cn != 0 || mn != 0 || sig != 0)
for (Pipelines::const_iterator i = pipelines.begin()+1; i != pipelines.end(); ++i)
if ((x = (*i)->method.pass(cn, mn, sig)) > xs)
pipeline = *i,
xs = x;
return pipeline;
}
PMF::HPipeline PMF::getPipeline (const char* name) const
{
// Common filter is always the first in the array of filters
assert(!pipelines.empty());
if (name == 0 || *name == 0)
return pipelines[0];
Pipelines::const_iterator ptr = pipelines.begin()+1,
end = pipelines.end();
for (; ptr != end; ++ptr)
if ((*ptr)->name == name)
return *ptr;
return 0;
}
const char* PMF::getArg (HPipeline hpipe, const char* key) const
{
vector<Str> keys;
for (StrTokenizer tokens('.', key, key + strlen(key)); tokens.next();)
keys.push_back(tokens.token);
const Cmd* cmdp = lookArg(hpipe, &keys[0], keys.size());
if (cmdp != 0)
return cmdp->right.ptr;
return 0;
}
Action* PMF::getAction (HPipeline hpipe, const char* path)
{
vector<Str> fqname;
for (StrTokenizer tokens('.', path, path + strlen(path)); tokens.next();)
fqname.push_back(tokens.token);
Pipeline::Steps::iterator ptr = hpipe->steps->begin(),
end = hpipe->steps->end();
for (; ptr != end; ++ptr)
{
Pipeline::Step& step = *ptr;
if (step.fqname->size() == fqname.size() &&
compare(&step.fqname->at(0), &fqname.at(0), fqname.size()))
return step.action;
}
return 0;
}
void PMF::walk (Pipeline& pipeline, Pipeline::Alias* aliasp, Strs& fqname)
{
Pipeline::Alias::Childs::iterator end = aliasp->childs.end(),
ptr = aliasp->childs.begin();
for (; ptr != end; ++ptr)
{
Pipeline::Alias::Child& child = *ptr;
fqname.push_back(child.name);
//cout << "walk " << fqname << endl;;
bool goon = (child.type != Pipeline::Alias::Child::DEFOFF);
const Cmd* cmdp = lookArg(&pipeline, &fqname[0], fqname.size());
if (cmdp != 0)
{
if (cmdp->right == "on")
{
goon = true;
}
else if (cmdp->right == "off")
{
if (ptr->type == Pipeline::Alias::Child::REQUIRED)
cmdp->fatal("This path item cannot be off");
goon = false;
}
else
cmdp->fatal("Invalid path item selection");
}
if (goon) {
if (child.aliasp != 0)
walk(pipeline, child.aliasp, fqname);
else
{
Pipeline::Step step;
if ((step.factory = IActionFactory::find(child.name)) == 0)
crash("PMF: Action '%s' not found\n", c_str(mm, child.name));
step.pipeline = &pipeline;
step.fqname = new (mm) Strs(mm, fqname.size());
*step.fqname = fqname;
pipeline.steps->push_back(step);
}
}
fqname.pop_back();
}
}
const PMF::Cmd* PMF::lookArg (Pipeline* pipeline, const Str* fqname, size_t fqsize) const
{
const Cmd* cmdp = 0;
int sx = 0;
for (Cmds::const_iterator it = cmds.begin(); it != cmds.end(); ++it)
if (*it != 0 && (*it)->arg)
{
const Cmd& cmd = **it;
if (pipeline == 0 || pipeline->name.empty())
{
if (!cmd.filtername.empty())
continue;
}
else
{
if (!cmd.filtername.empty() && !(cmd.filtername == pipeline->name))
continue;
}
if (cmd.left->size() - cmd.xkeyword -1 != fqsize)
continue;
if (!compare(&cmd.left->at(cmd.xkeyword+1), fqname, fqsize))
continue;
int s = cmd.strength();
if (cmdp == 0)
cmdp = &cmd,
sx = s;
else
{
if (s == sx)
crash("Ambigiguity between commands '%s' and '%s'", cmdp->buff, cmd.buff);
if (s > sx)
cmdp = &cmd,
sx = s;
}
}
return cmdp;
}
PMF::Pipeline* PMF::lookup (Str* name, bool create)
{
// Common filter is always the first in the array of filters
assert(!pipelines.empty());
if (name == 0 || name->empty())
return pipelines[0];
Pipelines::iterator ptr = pipelines.begin()+1,
end = pipelines.end();
for (; ptr != end; ++ptr)
if ((*ptr)->name == *name)
return *ptr;
if (!create)
return 0;
// add required filter
Pipeline* pipeline = new (mm) Pipeline(*this);
pipeline->name = *name;
pipelines.push_back(pipeline);
return pipeline;
}
static bool compFactories (IActionFactory* a, IActionFactory* b)
{
return strcmp(a->getName(), b->getName()) < 0;
}
void PMF::showHelp (std::ostream& os)
{
IActionFactory* afp;
if ((afp = IActionFactory::find(help_requested)) != 0)
afp->showHelp(os);
else
{
typedef vector<IActionFactory*> Factories;
Factories tmp;
for (afp = IActionFactory::getFirst(); afp != 0; afp = afp->getNext())
tmp.push_back(afp);
sort(tmp.begin(), tmp.end(), compFactories);
os << endl << "Help for Jitrino Actions (when available)" << endl;
for (Factories::iterator it = tmp.begin(); it != tmp.end(); ++it)
(*it)->showHelp(os);
}
}
void PMF::showHelpJits (std::ostream& os)
{
os << endl << "Jit " << jitname << endl;
for (Pipelines::iterator k = pipelines.begin(); k != pipelines.end(); ++k)
{
Pipeline& pipeline = **k;
os << " Pipeline ";
if (&pipeline == pipelines.front())
os << "<common>";
else
os << pipeline.name
<< " filter " << pipeline.method.classname
<< "." << pipeline.method.methodname
<< pipeline.method.signature;
os << endl;
// Print pipeline-wide args
for (Cmds::const_iterator i = cmds.begin(); i != cmds.end(); ++i)
if (*i != 0 && (*i)->arg)
{
const Cmd& cmd = **i;
if (cmd.filtername.empty() || (cmd.filtername == pipeline.name))
if (cmd.left->size() - cmd.xkeyword == 2)
os << " arg " << cmd.left->at(cmd.xkeyword + 1) << "=" << cmd.right << endl;
}
// Print pipeline-wide streams
for (size_t idx = 0; idx != logtemplates.size(); ++idx)
{
LogTemplate& lt = logtemplates[idx];
if (lt.enabled && (lt.filtername == pipeline.name))
if (lt.pathp == 0 || lt.pathp->empty())
{
os << " stream " << lt.streamname
<< "[" << idx << "]"
<< " file " << lt.fmask
<< endl;
}
}
// Print list of actions and specific args/streams
if (!pipeline.steps->empty())
{
os << " Actions" << endl;
for (Pipeline::Steps::iterator l = pipeline.steps->begin(); l != pipeline.steps->end(); ++l)
{
Pipeline::Step& step = *l;
os << " " << *step.fqname << endl;
if (step.args != 0)
for (Args::Store::iterator m = step.args->store.begin(); m != step.args->store.end(); ++m)
{
Args::Arg& arg = *m;
// Do not print pipeline-wide args
if (arg.cmdp->left->size() - arg.cmdp->xkeyword > 2)
os << " arg " << arg.key << "=" << arg.value << endl;
}
if (step.logs != 0)
for (size_t sid = 0; sid < step.logs->streamidxs.size(); ++sid)
{
size_t idx = step.logs->streamidxs.at(sid);
LogTemplate& lt = logtemplates.at(idx);
if (lt.enabled)
if (lt.pathp != 0 && !lt.pathp->empty())
os << " stream " << lt.streamname
<< "[" << idx << "]"
<< " file " << lt.fmask
<< endl;
}
}
}
}
}
void PMF::summTimes (SummTimes& summtimes)
{
Pipelines::iterator pptr = pipelines.begin(),
pend = pipelines.end();
for (; pptr != pend; ++pptr)
{
Pipeline::Steps::iterator sptr = (*pptr)->steps->begin(),
send = (*pptr)->steps->end();
for (; sptr != send; ++sptr)
summtimes.add(sptr->factory->getName(), sptr->getSeconds());
}
}
//------ Pipeline implementation --------------------------------------------//
PMF::Pipeline::Pipeline (PMF& p)
:pmf(p), initialized(false), root(0)
{
aliases = new (pmf.mm) Aliases(pmf.mm);
steps = new (pmf.mm) Steps(pmf.mm);
}
PMF::Pipeline::~Pipeline ()
{
}
void PMF::Pipeline::init ()
{
if (!initialized)
{
initialized = true;
Pipeline::Steps::iterator ptr = steps->begin(),
end = steps->end();
for (; ptr != end; ++ptr)
{
Pipeline::Step& step = *ptr;
if (step.action != 0 && !step.reused)
{
step.action->step = &step;
step.action->init();
}
}
}
}
void PMF::Pipeline::deinit ()
{
if (initialized)
{
initialized = false;
Pipeline::Steps::iterator ptr = steps->begin(),
end = steps->end();
for (; ptr != end; ++ptr)
{
Pipeline::Step& step = *ptr;
if (step.action != 0 && !step.reused)
step.action->deinit();
}
}
}
PMF::Pipeline::Alias* PMF::Pipeline::lookup (Str* name)
{
Aliases::iterator ptr = aliases->begin(),
end = aliases->end();
for (; ptr != end; ++ptr)
{
Alias* aliasp = *ptr;
if ((name != 0 && *name == aliasp->name) ||
(name == 0 && aliasp->name.empty()))
return aliasp;
}
return 0;
}
PMF::Pipeline::Alias* PMF::Pipeline::findPath (Str* names, size_t count)
{
Alias* aliasp = root;
for (; aliasp != 0 && count != 0; --count)
{
Alias::Childs::iterator end = aliasp->childs.end(),
ptr = find(aliasp->childs.begin(), end, *names);
if (ptr == end)
return 0;
aliasp = ptr->aliasp;
++names;
}
return aliasp;
}
void PMF::Pipeline::stop (const char* msg)
{
crash(msg, c_str(pmf.mm, pmf.jitname), c_str(pmf.mm, name));
}
//------ PMF::Pipeline::Step implementation ---------------------------------//
void PMF::Pipeline::Step::setup (MemoryManager& mm)
{
args = 0;
for (ArgIterator it(*this, 0); it.next();)
{
Cmd& cmd = *it;
if (args == 0)
args = new (mm) Args(mm);
const char* key = c_str(cmd.left->at(cmd.left->size() - 1));
const char* value = c_str(cmd.right);
int strength = cmd.strength(cmd.left->size() - cmd.xkeyword - 1);
args->add(key, value, strength, &cmd);
}
// all templates in scope for this step
vector<LogTemplate*> acts;
LogTemplates& logtemplates = pipeline->pmf.logtemplates;
for (size_t sx = 0; sx != logtemplates.size(); ++sx)
{
LogTemplate& lt = logtemplates[sx];
if (!lt.enabled)
continue;
if (!(lt.filtername == pipeline->name))
continue;
const size_t pathsz = lt.pathp->size();
if (pathsz > fqname->size())
continue;
if (pathsz == 1 && lt.pathp->at(0) == factory->getName())
;
else if (pathsz != 0 && !compare(&lt.pathp->at(0), &fqname->at(0), lt.pathp->size()))
continue;
vector<LogTemplate*>::iterator it, end = acts.end();
for (it = acts.begin(); it != end; ++it)
if ((*it)->streamname == lt.streamname)
break;
if (it == end)
acts.push_back(&lt);
else if (cmd_strength(false, !(*it)->filtername.empty(), (*it)->pathp->size())
< cmd_strength(false, !lt.filtername.empty(), pathsz))
*it = &lt; // this definition is the strongest
}
logs = 0;
if (!acts.empty())
{
logs = new (mm) LogDisplay(mm);
vector<LogTemplate*>::iterator it, end = acts.end();
for (it = acts.begin(); it != end; ++it)
{
LogTemplate lt = **it;
logs->add(lt.idx, lt);
}
}
}
//------ PMF::PipelineIterator implementation -------------------------------//
PMF::PipelineIterator::PipelineIterator (PMF& p, const char* cn, const char* mn, const char* sig)
:pmf(p), mm("PMF::PipelineIterator"), smm(0), session(0), first(true)
{
if (pmf.pipelines.empty())
crash("PMF: No pipelines defined\n");
pipeline = pmf.selectPipeline(cn, mn, sig);
pipeline->init();
}
PMF::PipelineIterator::PipelineIterator (HPipeline hpipe)
:pmf(hpipe->pmf), pipeline(hpipe), mm("PMF::PipelineIterator"), smm(0), session(0), first(true)
{
pipeline->init();
}
PMF::PipelineIterator::~PipelineIterator ()
{
}
bool PMF::PipelineIterator::next ()
{
Pipeline::Steps::iterator end = pipeline->steps->end();
if (first)
{
first = false;
it = pipeline->steps->begin();
}
else
{
assert(it != end);
// close previous session
session->step = 0;
session->~SessionAction();
session = 0;
delete smm;
// go to new session
++it;
}
if (it != end)
{// open new session
smm = new MemoryManager(it->factory->getName());
session = it->factory->createSessionAction(*smm);
assert(session != 0);
session->step = &*it;
return true;
}
else
return false;
}
const char* PMF::PipelineIterator::getPipeName (MemoryManager& mm) const
{
assert(pipeline != 0);
return c_str(mm, pipeline->name);
}
const char* PMF::PipelineIterator::getFilterSpec (MemoryManager& mm) const
{
assert(pipeline != 0);
return pipeline->method.c_str(mm);
}
const char* PMF::PipelineIterator::getStepName () const
{
assert(!first && it != pipeline->steps->end());
return c_str(*smm, &it->fqname->at(0), it->fqname->size());
}
//------ ActionFactory implementation ---------------------------------------//
IActionFactory* IActionFactory::head = 0;
IActionFactory::IActionFactory (const char* n)
:name(n)
{
Str sname(name);
if (find(sname) != 0)
crash("PMF: duplicate Action '%s'\n", name);
next = head;
head = this;
}
void IActionFactory::showHelp (ostream& os, const char* help) const
{
if (help != 0)
os << endl << getName() << endl
<< help;
}
IActionFactory* IActionFactory::find (Str& str)
{
for (IActionFactory* p = getFirst(); p != 0; p = p->getNext())
if (p->name == str)
return p;
return 0;
}
//------ IAction implementation ---------------------------------------------//
bool IAction::getArg (const char* key, unsigned int& v) const
{
const char* arg = getArg(key);
if (arg == 0)
return false;
else
{
v = atoi(arg);
return true;
}
}
bool IAction::getArg (const char* key, unsigned long& v) const
{
const char* arg = getArg(key);
if (arg == 0)
return false;
else
{
v = atol(arg);
return true;
}
}
bool IAction::getArg (const char* key, bool& v) const
{
const char* arg = getArg(key);
if (arg == 0)
return false;
else
{
if (strcmp(arg, "true") == 0 || strcmp(arg, "yes") == 0)
v = true;
else if (strcmp(arg, "false") == 0 || strcmp(arg, "no") == 0)
v = false;
else
crash("PMF: invalid value for bool parameter '%s' (true|false or yes|no expected)\n", key);
return true;
}
}
const char* IAction::getArg (HPipeline hp, const char* key) const
{
return step->pipeline->pmf.getArg(const_cast<PMF::HPipeline>(hp), key);
}
#ifndef _NOLOG
bool IAction::isLogEnabled (LogStream::SID sid) const
{
return step->logs != 0 && step->logs->log(sid).isEnabled();
}
#endif
#ifndef _NOLOG
bool IAction::isLogEnabled (const char* streamname) const
{
LogStream::SID sid;
if (getLogStreamID(sid, streamname))
return isLogEnabled(sid);
else
return false;
}
#endif
#ifndef _NOLOG
LogStream& IAction::log (LogStream::SID sid) const
{
CompilationContext* currentCC = CompilationContext::getCurrentContext();
CompilationContext* topLevelCC = CompilationContext::getCurrentContext()->getVMCompilationInterface()->getCompilationContext();
const IAction* action = this;
if (currentCC != topLevelCC) {
action = topLevelCC->getCurrentSessionAction();
}
return (action == NULL || action->step->logs == NULL) ? LogStream::log_sink() : action->step->logs->log(sid);
}
#endif
#ifndef _NOLOG
LogStream& IAction::log (const char* streamname) const
{
LogStream::SID sid;
if (getLogStreamID(sid, streamname))
return log(sid);
else
return LogStream::log_sink();
}
#endif
bool IAction::getLogStreamID (LogStream::SID& sid, const char* streamname) const
{
if ((step->logs) == 0)
return false;
LogTemplates& logtemplates = step->pipeline->pmf.logtemplates;
LogDisplay::StreamIdxs& idxs = step->logs->streamidxs;
for (size_t i = 0; i != idxs.size(); ++i)
if (logtemplates[idxs[i]].streamname == streamname)
{
sid = static_cast<LogStream::SID>(i);
return true;
}
return false;
}
//------ Action implementation ----------------------------------------------//
Action* Action::createAuxAction (const char* name, const char* suffix) const
{
MemoryManager& mm = step->pipeline->pmf.mm;
return createAuxAction (mm, name, suffix);
}
Action* Action::createAuxAction (MemoryManager& mm, const char* name, const char* suffix) const
{
PMF::Pipeline::Step* auxstep = new (mm) PMF::Pipeline::Step;
auxstep->factory = step->factory;
auxstep->pipeline = getPipeline();
Strs* fqname = new (mm) Strs(mm);
fqname->insert(fqname->end(), step->fqname->begin(), step->fqname->end());
fqname->push_back(Str(suffix));
auxstep->fqname = fqname;
Str sname(name);
auxstep->factory = IActionFactory::find(sname);
assert(auxstep->factory != 0);
Action* auxaction = auxstep->factory->createAction(mm);
auxaction->step = auxstep;
auxstep->action = auxaction;
auxstep->setup(mm);
return auxaction;
}
SessionAction* Action::createSession (MemoryManager& mm)
{
SessionAction* session = step->factory->createSessionAction(mm);
assert(session != 0);
session->step = step;
return session;
}
/*
void Action::destroySession (SessionAction* session)
{
session->step = 0;
session->~SessionAction();
}
*/
//---------------------------------------------------------------------------//
} //namespace Jitrino