diff options
author | Brad King <brad.king@kitware.com> | 2016-04-14 15:24:08 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2016-04-18 13:21:19 (GMT) |
commit | 7c36d2067b00996fcead56310f4fc4fa58434247 (patch) | |
tree | 6f0b8771c11e3cb6b20e91680b0b17114c52bb06 /Source/cmListFileCache.cxx | |
parent | 1f6bd8a93f602f9d4dde1c3ea697211897f25827 (diff) | |
download | CMake-7c36d2067b00996fcead56310f4fc4fa58434247.zip CMake-7c36d2067b00996fcead56310f4fc4fa58434247.tar.gz CMake-7c36d2067b00996fcead56310f4fc4fa58434247.tar.bz2 |
cmListFileBacktrace: Refactor storage to provide efficient value semantics
Since commit v3.4.0-rc1~321^2~2 (Genex: Store a backtrace, not a pointer
to one, 2015-07-08) we treat cmListFileBacktrace instances as
lightweight values. This was true at the time only because the
backtrace information was kept in the cmState snapshot hierarchy.
However, that forced us to accumulate a lot of otherwise short-lived
snapshots just to have the backtrace fields available for reference by
cmListFileBacktrace instances. Recent refactoring made backtrace
instances independent of the snapshot hierarchy to avoid accumulating
short-lived snapshots. This came at the cost of making backtrace values
heavy again, leading to lots of string coying and slower execution.
Fix this by refactoring cmListFileBacktrace to provide value semantics
with efficient shared storage underneath. Teach cmMakefile to maintain
its call stack using an instance of cmListFileBacktrace. This approach
allows the current backtrace to be efficiently saved whenever it is
needed.
Also teach cmListFileBacktrace the notion of a file-level scope. This
is useful for messages about the whole file (e.g. during parsing) that
are not specific to any line within it. Push the CMakeLists.txt scope
for each directory and never pop it. This ensures that we always have
some context information and simplifies cmMakefile::IssueMessage.
Push/pop a file-level scope as each included file is processed. This
supersedes cmParseFileScope and improves diagnostic message context
information in a few places. Fix the corresponding test cases to expect
the improved output.
Diffstat (limited to 'Source/cmListFileCache.cxx')
-rw-r--r-- | Source/cmListFileCache.cxx | 171 |
1 files changed, 135 insertions, 36 deletions
diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index f198ac3..1cc66f4 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -398,73 +398,172 @@ bool cmListFileParser::AddArgument(cmListFileLexer_Token* token, } } -cmListFileBacktrace::cmListFileBacktrace(cmState::Snapshot snapshot, - cmCommandContext const& cc) - : Snapshot(snapshot) +struct cmListFileBacktrace::Entry: public cmListFileContext { - if (!this->Snapshot.IsValid()) + Entry(cmListFileContext const& lfc, Entry* up): + cmListFileContext(lfc), Up(up), RefCount(0) { - return; + if (this->Up) + { + this->Up->Ref(); + } } - - // Record the entire call stack now so that the `Snapshot` we - // save for later refers to a long-lived scope. This avoids - // having to keep short-lived scopes around just to extract - // their backtrace information later. - - cmListFileContext lfc = - cmListFileContext::FromCommandContext( - cc, this->Snapshot.GetExecutionListFile()); - this->push_back(lfc); - - cmState::Snapshot parent = this->Snapshot.GetCallStackParent(); - while (parent.IsValid()) + ~Entry() + { + if (this->Up) + { + this->Up->Unref(); + } + } + void Ref() + { + ++this->RefCount; + } + void Unref() { - lfc.Name = this->Snapshot.GetEntryPointCommand(); - lfc.Line = this->Snapshot.GetEntryPointLine(); - lfc.FilePath = parent.GetExecutionListFile(); - if (lfc.FilePath.empty()) + if (--this->RefCount == 0) { - break; + delete this; } - this->push_back(lfc); + } + Entry* Up; + unsigned int RefCount; +}; - this->Snapshot = parent; - parent = parent.GetCallStackParent(); +cmListFileBacktrace::cmListFileBacktrace(cmState::Snapshot bottom, + Entry* up, + cmListFileContext const& lfc): + Bottom(bottom), Cur(new Entry(lfc, up)) +{ + assert(this->Bottom.IsValid()); + this->Cur->Ref(); +} + +cmListFileBacktrace::cmListFileBacktrace(cmState::Snapshot bottom, Entry* cur): + Bottom(bottom), Cur(cur) +{ + if (this->Cur) + { + assert(this->Bottom.IsValid()); + this->Cur->Ref(); } +} + +cmListFileBacktrace::cmListFileBacktrace(): Bottom(), Cur(0) +{ +} + +cmListFileBacktrace::cmListFileBacktrace(cmState::Snapshot snapshot): + Bottom(snapshot.GetCallStackBottom()), Cur(0) +{ +} + +cmListFileBacktrace::cmListFileBacktrace(cmListFileBacktrace const& r): + Bottom(r.Bottom), Cur(r.Cur) +{ + if (this->Cur) + { + assert(this->Bottom.IsValid()); + this->Cur->Ref(); + } +} - this->Snapshot = this->Snapshot.GetCallStackBottom(); +cmListFileBacktrace& +cmListFileBacktrace::operator=(cmListFileBacktrace const& r) +{ + cmListFileBacktrace tmp(r); + std::swap(this->Cur, tmp.Cur); + std::swap(this->Bottom, tmp.Bottom); + return *this; } cmListFileBacktrace::~cmListFileBacktrace() { + if (this->Cur) + { + this->Cur->Unref(); + } +} + +cmListFileBacktrace +cmListFileBacktrace::Push(std::string const& file) const +{ + // We are entering a file-level scope but have not yet reached + // any specific line or command invocation within it. This context + // is useful to print when it is at the top but otherwise can be + // skipped during call stack printing. + cmListFileContext lfc; + lfc.FilePath = file; + return cmListFileBacktrace(this->Bottom, this->Cur, lfc); +} + +cmListFileBacktrace +cmListFileBacktrace::Push(cmListFileContext const& lfc) const +{ + return cmListFileBacktrace(this->Bottom, this->Cur, lfc); +} + +cmListFileBacktrace cmListFileBacktrace::Pop() const +{ + assert(this->Cur); + return cmListFileBacktrace(this->Bottom, this->Cur->Up); +} + +cmListFileContext const& cmListFileBacktrace::Top() const +{ + if (this->Cur) + { + return *this->Cur; + } + else + { + static cmListFileContext const empty; + return empty; + } } void cmListFileBacktrace::PrintTitle(std::ostream& out) const { - if (this->empty()) + if (!this->Cur) { return; } - cmOutputConverter converter(this->Snapshot); - cmListFileContext lfc = this->front(); - lfc.FilePath = converter.Convert(lfc.FilePath, cmOutputConverter::HOME); + cmOutputConverter converter(this->Bottom); + cmListFileContext lfc = *this->Cur; + if (!this->Bottom.GetState()->GetIsInTryCompile()) + { + lfc.FilePath = converter.Convert(lfc.FilePath, cmOutputConverter::HOME); + } out << (lfc.Line ? " at " : " in ") << lfc; } void cmListFileBacktrace::PrintCallStack(std::ostream& out) const { - if (this->size() <= 1) + if (!this->Cur || !this->Cur->Up) { return; } - out << "Call Stack (most recent call first):\n"; - cmOutputConverter converter(this->Snapshot); - for (const_iterator i = this->begin() + 1; i != this->end(); ++i) + bool first = true; + cmOutputConverter converter(this->Bottom); + for (Entry* i = this->Cur->Up; i; i = i->Up) { + if (i->Name.empty()) + { + // Skip this whole-file scope. When we get here we already will + // have printed a more-specific context within the file. + continue; + } + if (first) + { + first = false; + out << "Call Stack (most recent call first):\n"; + } cmListFileContext lfc = *i; - lfc.FilePath = converter.Convert(lfc.FilePath, cmOutputConverter::HOME); + if (!this->Bottom.GetState()->GetIsInTryCompile()) + { + lfc.FilePath = converter.Convert(lfc.FilePath, cmOutputConverter::HOME); + } out << " " << lfc << "\n"; } } |