| /* |
| * 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), <.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(<.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(<); |
| else if (cmd_strength(false, !(*it)->filtername.empty(), (*it)->pathp->size()) |
| < cmd_strength(false, !lt.filtername.empty(), pathsz)) |
| *it = < // 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 |