summaryrefslogtreecommitdiffstats
path: root/sc/source/core/tool/formulalogger.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/tool/formulalogger.cxx')
-rw-r--r--sc/source/core/tool/formulalogger.cxx357
1 files changed, 357 insertions, 0 deletions
diff --git a/sc/source/core/tool/formulalogger.cxx b/sc/source/core/tool/formulalogger.cxx
new file mode 100644
index 000000000..46465b561
--- /dev/null
+++ b/sc/source/core/tool/formulalogger.cxx
@@ -0,0 +1,357 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <formulalogger.hxx>
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <document.hxx>
+#include <tokenstringcontext.hxx>
+#include <address.hxx>
+#include <interpre.hxx>
+
+#include <osl/file.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <formula/vectortoken.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <cstdlib>
+
+namespace sc {
+
+namespace {
+
+std::unique_ptr<osl::File> initFile()
+{
+ const char* pPath = std::getenv("LIBO_FORMULA_LOG_FILE");
+ if (!pPath)
+ return nullptr;
+
+ // Support both file:///... and system file path notations.
+ OUString aPath = OUString::createFromAscii(pPath);
+ INetURLObject aURL;
+ aURL.SetSmartURL(aPath);
+ aPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ return std::make_unique<osl::File>(aPath);
+}
+
+ScRefFlags getRefFlags( const ScAddress& rCellPos, const ScAddress& rRefPos )
+{
+ ScRefFlags eFlags = ScRefFlags::VALID;
+ if (rCellPos.Tab() != rRefPos.Tab())
+ eFlags |= ScRefFlags::TAB_3D;
+ return eFlags;
+}
+
+}
+
+FormulaLogger& FormulaLogger::get()
+{
+ static FormulaLogger aLogger;
+ return aLogger;
+}
+
+struct FormulaLogger::GroupScope::Impl
+{
+ FormulaLogger& mrLogger;
+ const ScDocument& mrDoc;
+
+ OUString maPrefix;
+ std::vector<OUString> maMessages;
+
+ bool mbCalcComplete;
+ bool mbOutputEnabled;
+
+ Impl( FormulaLogger& rLogger, const OUString& rPrefix, const ScDocument& rDoc,
+ const ScFormulaCell& rCell, bool bOutputEnabled ) :
+ mrLogger(rLogger), mrDoc(rDoc), maPrefix(rPrefix),
+ mbCalcComplete(false), mbOutputEnabled(bOutputEnabled)
+ {
+ ++mrLogger.mnNestLevel;
+
+ if (!mbOutputEnabled)
+ return;
+
+ sc::TokenStringContext aCxt(rDoc, rDoc.GetGrammar());
+ OUString aFormula = rCell.GetCode()->CreateString(aCxt, rCell.aPos);
+
+ mrLogger.write(maPrefix);
+ mrLogger.writeNestLevel();
+
+ mrLogger.writeAscii("-- enter (formula='");
+ mrLogger.write(aFormula);
+ mrLogger.writeAscii("', size=");
+ mrLogger.write(rCell.GetSharedLength());
+ mrLogger.writeAscii(")\n");
+ }
+
+ ~Impl()
+ {
+ if (mbOutputEnabled)
+ {
+ for (const OUString& rMsg : maMessages)
+ {
+ mrLogger.write(maPrefix);
+ mrLogger.writeNestLevel();
+ mrLogger.writeAscii(" * ");
+ mrLogger.write(rMsg);
+ mrLogger.writeAscii("\n");
+ }
+
+ mrLogger.write(maPrefix);
+ mrLogger.writeNestLevel();
+ mrLogger.writeAscii("-- exit (");
+ if (mbCalcComplete)
+ mrLogger.writeAscii("calculation complete");
+ else
+ mrLogger.writeAscii("without calculation");
+
+ mrLogger.writeAscii(")\n");
+
+ mrLogger.sync();
+ }
+
+ --mrLogger.mnNestLevel;
+ }
+};
+
+FormulaLogger::GroupScope::GroupScope(
+ FormulaLogger& rLogger, const OUString& rPrefix, const ScDocument& rDoc,
+ const ScFormulaCell& rCell, bool bOutputEnabled ) :
+ mpImpl(std::make_unique<Impl>(rLogger, rPrefix, rDoc, rCell, bOutputEnabled)) {}
+
+FormulaLogger::GroupScope::GroupScope(GroupScope&& r) noexcept : mpImpl(std::move(r.mpImpl)) {}
+
+FormulaLogger::GroupScope::~GroupScope() {}
+
+void FormulaLogger::GroupScope::addMessage( const OUString& rMsg )
+{
+ mpImpl->maMessages.push_back(rMsg);
+}
+
+void FormulaLogger::GroupScope::addRefMessage(
+ const ScAddress& rCellPos, const ScAddress& rRefPos, size_t nLen,
+ const formula::VectorRefArray& rArray )
+{
+ OUStringBuffer aBuf;
+
+ ScRange aRefRange(rRefPos);
+ aRefRange.aEnd.IncRow(nLen-1);
+ OUString aRangeStr = aRefRange.Format(mpImpl->mrDoc, getRefFlags(rCellPos, rRefPos));
+ aBuf.append(aRangeStr);
+ aBuf.append(": ");
+
+ if (rArray.mpNumericArray)
+ {
+ if (rArray.mpStringArray)
+ {
+ // mixture of numeric and string cells.
+ aBuf.append("numeric and string");
+ }
+ else
+ {
+ // numeric cells only.
+ aBuf.append("numeric only");
+ }
+ }
+ else
+ {
+ if (rArray.mpStringArray)
+ {
+ // string cells only.
+ aBuf.append("string only");
+ }
+ else
+ {
+ // empty cells.
+ aBuf.append("empty");
+ }
+ }
+
+ mpImpl->maMessages.push_back(aBuf.makeStringAndClear());
+}
+
+void FormulaLogger::GroupScope::addRefMessage(
+ const ScAddress& rCellPos, const ScAddress& rRefPos, size_t nLen,
+ const std::vector<formula::VectorRefArray>& rArrays )
+{
+ ScAddress aPos(rRefPos); // copy
+ for (const formula::VectorRefArray& rArray : rArrays)
+ {
+ addRefMessage(rCellPos, aPos, nLen, rArray);
+ aPos.IncCol();
+ }
+}
+
+void FormulaLogger::GroupScope::addRefMessage(
+ const ScAddress& rCellPos, const ScAddress& rRefPos,
+ const formula::FormulaToken& rToken )
+{
+ OUStringBuffer aBuf;
+ OUString aPosStr = rRefPos.Format(getRefFlags(rCellPos, rRefPos), &mpImpl->mrDoc);
+ aBuf.append(aPosStr);
+ aBuf.append(": ");
+
+ switch (rToken.GetType())
+ {
+ case formula::svDouble:
+ aBuf.append("numeric value");
+ break;
+ case formula::svString:
+ aBuf.append("string value");
+ break;
+ default:
+ aBuf.append("unknown value");
+ }
+
+ mpImpl->maMessages.push_back(aBuf.makeStringAndClear());
+}
+
+void FormulaLogger::GroupScope::addGroupSizeThresholdMessage( const ScFormulaCell& rCell )
+{
+ OUStringBuffer aBuf;
+ aBuf.append("group length below minimum threshold (");
+ aBuf.append(rCell.GetWeight());
+ aBuf.append(" < ");
+ aBuf.append(ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize);
+ aBuf.append(")");
+ mpImpl->maMessages.push_back(aBuf.makeStringAndClear());
+}
+
+void FormulaLogger::GroupScope::setCalcComplete()
+{
+ mpImpl->mbCalcComplete = true;
+ addMessage("calculation performed");
+}
+
+FormulaLogger::FormulaLogger()
+{
+ mpLogFile = initFile();
+
+ if (!mpLogFile)
+ return;
+
+ osl::FileBase::RC eRC = mpLogFile->open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
+
+ if (eRC == osl::FileBase::E_EXIST)
+ {
+ eRC = mpLogFile->open(osl_File_OpenFlag_Write);
+
+ if (eRC != osl::FileBase::E_None)
+ {
+ // Failed to open an existing log file.
+ mpLogFile.reset();
+ return;
+ }
+
+ if (mpLogFile->setPos(osl_Pos_End, 0) != osl::FileBase::E_None)
+ {
+ // Failed to set the position to the end of the file.
+ mpLogFile.reset();
+ return;
+ }
+ }
+ else if (eRC != osl::FileBase::E_None)
+ {
+ // Failed to create a new file.
+ mpLogFile.reset();
+ return;
+ }
+
+ // Output the header information.
+ writeAscii("---\n");
+ writeAscii("OpenCL: ");
+ writeAscii(ScCalcConfig::isOpenCLEnabled() ? "enabled\n" : "disabled\n");
+ writeAscii("---\n");
+
+ sync();
+}
+
+FormulaLogger::~FormulaLogger()
+{
+ if (mpLogFile)
+ mpLogFile->close();
+}
+
+void FormulaLogger::writeAscii( const char* s )
+{
+ if (!mpLogFile)
+ return;
+
+ sal_uInt64 nBytes;
+ mpLogFile->write(s, strlen(s), nBytes);
+}
+
+void FormulaLogger::writeAscii( const char* s, size_t n )
+{
+ if (!mpLogFile)
+ return;
+
+ sal_uInt64 nBytes;
+ mpLogFile->write(s, n, nBytes);
+}
+
+void FormulaLogger::write( std::u16string_view ou )
+{
+ OString s = OUStringToOString(ou, RTL_TEXTENCODING_UTF8).getStr();
+ writeAscii(s.getStr(), s.getLength());
+}
+
+void FormulaLogger::write( sal_Int32 n )
+{
+ OString s = OString::number(n);
+ writeAscii(s.getStr(), s.getLength());
+}
+
+void FormulaLogger::sync()
+{
+ if (!mpLogFile)
+ return;
+
+ mpLogFile->sync();
+}
+
+void FormulaLogger::writeNestLevel()
+{
+ // Write the nest level, but keep it only 1-character length to avoid
+ // messing up the spacing.
+ if (mnNestLevel < 10)
+ write(mnNestLevel);
+ else
+ writeAscii("!");
+
+ writeAscii(": ");
+ for (sal_Int32 i = 1; i < mnNestLevel; ++i)
+ writeAscii(" ");
+}
+
+FormulaLogger::GroupScope FormulaLogger::enterGroup(
+ const ScDocument& rDoc, const ScFormulaCell& rCell )
+{
+ // Get the file name if available.
+ const SfxObjectShell* pShell = rDoc.GetDocumentShell();
+ const SfxMedium* pMedium = pShell ? pShell->GetMedium() : nullptr;
+ OUString aName;
+ if (pMedium)
+ aName = pMedium->GetURLObject().GetLastName();
+ if (aName.isEmpty())
+ aName = "-"; // unsaved document.
+
+ OUString aGroupPrefix = aName + ": formula-group: " +
+ rCell.aPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDoc, rDoc.GetAddressConvention()) + ": ";
+
+ bool bOutputEnabled = mpLastGroup != rCell.GetCellGroup().get();
+ mpLastGroup = rCell.GetCellGroup().get();
+
+ return GroupScope(*this, aGroupPrefix, rDoc, rCell, bOutputEnabled);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */