LibreOffice Module sc (master)  1
formulalogger.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6  */
7 
8 #include <formulalogger.hxx>
9 #include <formulacell.hxx>
10 #include <tokenarray.hxx>
11 #include <document.hxx>
12 #include <tokenstringcontext.hxx>
13 #include <address.hxx>
14 #include <interpre.hxx>
15 
16 #include <osl/file.hxx>
17 #include <sfx2/objsh.hxx>
18 #include <sfx2/docfile.hxx>
19 #include <tools/urlobj.hxx>
20 #include <formula/vectortoken.hxx>
21 #include <rtl/ustrbuf.hxx>
22 
23 #include <cstdlib>
24 
25 namespace sc {
26 
27 namespace {
28 
29 std::unique_ptr<osl::File> initFile()
30 {
31  const char* pPath = std::getenv("LIBO_FORMULA_LOG_FILE");
32  if (!pPath)
33  return nullptr;
34 
35  // Support both file:///... and system file path notations.
36  OUString aPath = OUString::createFromAscii(pPath);
38  aURL.SetSmartURL(aPath);
40 
41  return std::make_unique<osl::File>(aPath);
42 }
43 
44 ScRefFlags getRefFlags( const ScAddress& rCellPos, const ScAddress& rRefPos )
45 {
47  if (rCellPos.Tab() != rRefPos.Tab())
48  eFlags |= ScRefFlags::TAB_3D;
49  return eFlags;
50 }
51 
52 }
53 
54 FormulaLogger& FormulaLogger::get()
55 {
56  static FormulaLogger aLogger;
57  return aLogger;
58 }
59 
61 {
63  const ScDocument& mrDoc;
64 
65  OUString maPrefix;
66  std::vector<OUString> maMessages;
67 
70 
71  Impl( FormulaLogger& rLogger, const OUString& rPrefix, const ScDocument& rDoc,
72  const ScFormulaCell& rCell, bool bOutputEnabled ) :
73  mrLogger(rLogger), mrDoc(rDoc), maPrefix(rPrefix),
74  mbCalcComplete(false), mbOutputEnabled(bOutputEnabled)
75  {
76  ++mrLogger.mnNestLevel;
77 
78  if (!mbOutputEnabled)
79  return;
80 
81  sc::TokenStringContext aCxt(rDoc, rDoc.GetGrammar());
82  OUString aFormula = rCell.GetCode()->CreateString(aCxt, rCell.aPos);
83 
84  mrLogger.write(maPrefix);
85  mrLogger.writeNestLevel();
86 
87  mrLogger.writeAscii("-- enter (formula='");
88  mrLogger.write(aFormula);
89  mrLogger.writeAscii("', size=");
90  mrLogger.write(rCell.GetSharedLength());
91  mrLogger.writeAscii(")\n");
92  }
93 
95  {
96  if (mbOutputEnabled)
97  {
98  for (const OUString& rMsg : maMessages)
99  {
100  mrLogger.write(maPrefix);
101  mrLogger.writeNestLevel();
102  mrLogger.writeAscii(" * ");
103  mrLogger.write(rMsg);
104  mrLogger.writeAscii("\n");
105  }
106 
107  mrLogger.write(maPrefix);
108  mrLogger.writeNestLevel();
109  mrLogger.writeAscii("-- exit (");
110  if (mbCalcComplete)
111  mrLogger.writeAscii("calculation complete");
112  else
113  mrLogger.writeAscii("without calculation");
114 
115  mrLogger.writeAscii(")\n");
116 
117  mrLogger.sync();
118  }
119 
120  --mrLogger.mnNestLevel;
121  }
122 };
123 
124 FormulaLogger::GroupScope::GroupScope(
125  FormulaLogger& rLogger, const OUString& rPrefix, const ScDocument& rDoc,
126  const ScFormulaCell& rCell, bool bOutputEnabled ) :
127  mpImpl(std::make_unique<Impl>(rLogger, rPrefix, rDoc, rCell, bOutputEnabled)) {}
128 
129 FormulaLogger::GroupScope::GroupScope(GroupScope&& r) noexcept : mpImpl(std::move(r.mpImpl)) {}
130 
131 FormulaLogger::GroupScope::~GroupScope() {}
132 
133 void FormulaLogger::GroupScope::addMessage( const OUString& rMsg )
134 {
135  mpImpl->maMessages.push_back(rMsg);
136 }
137 
139  const ScAddress& rCellPos, const ScAddress& rRefPos, size_t nLen,
140  const formula::VectorRefArray& rArray )
141 {
142  OUStringBuffer aBuf;
143 
144  ScRange aRefRange(rRefPos);
145  aRefRange.aEnd.IncRow(nLen-1);
146  OUString aRangeStr = aRefRange.Format(mpImpl->mrDoc, getRefFlags(rCellPos, rRefPos));
147  aBuf.append(aRangeStr);
148  aBuf.append(": ");
149 
150  if (rArray.mpNumericArray)
151  {
152  if (rArray.mpStringArray)
153  {
154  // mixture of numeric and string cells.
155  aBuf.append("numeric and string");
156  }
157  else
158  {
159  // numeric cells only.
160  aBuf.append("numeric only");
161  }
162  }
163  else
164  {
165  if (rArray.mpStringArray)
166  {
167  // string cells only.
168  aBuf.append("string only");
169  }
170  else
171  {
172  // empty cells.
173  aBuf.append("empty");
174  }
175  }
176 
177  mpImpl->maMessages.push_back(aBuf.makeStringAndClear());
178 }
179 
181  const ScAddress& rCellPos, const ScAddress& rRefPos, size_t nLen,
182  const std::vector<formula::VectorRefArray>& rArrays )
183 {
184  ScAddress aPos(rRefPos); // copy
185  for (const formula::VectorRefArray& rArray : rArrays)
186  {
187  addRefMessage(rCellPos, aPos, nLen, rArray);
188  aPos.IncCol();
189  }
190 }
191 
193  const ScAddress& rCellPos, const ScAddress& rRefPos,
194  const formula::FormulaToken& rToken )
195 {
196  OUStringBuffer aBuf;
197  OUString aPosStr = rRefPos.Format(getRefFlags(rCellPos, rRefPos), &mpImpl->mrDoc);
198  aBuf.append(aPosStr);
199  aBuf.append(": ");
200 
201  switch (rToken.GetType())
202  {
203  case formula::svDouble:
204  aBuf.append("numeric value");
205  break;
206  case formula::svString:
207  aBuf.append("string value");
208  break;
209  default:
210  aBuf.append("unknown value");
211  }
212 
213  mpImpl->maMessages.push_back(aBuf.makeStringAndClear());
214 }
215 
217 {
218  OUStringBuffer aBuf;
219  aBuf.append("group length below minimum threshold (");
220  aBuf.append(rCell.GetWeight());
221  aBuf.append(" < ");
222  aBuf.append(ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize);
223  aBuf.append(")");
224  mpImpl->maMessages.push_back(aBuf.makeStringAndClear());
225 }
226 
228 {
229  mpImpl->mbCalcComplete = true;
230  addMessage("calculation performed");
231 }
232 
233 FormulaLogger::FormulaLogger()
234 {
235  mpLogFile = initFile();
236 
237  if (!mpLogFile)
238  return;
239 
240  osl::FileBase::RC eRC = mpLogFile->open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
241 
242  if (eRC == osl::FileBase::E_EXIST)
243  {
244  eRC = mpLogFile->open(osl_File_OpenFlag_Write);
245 
246  if (eRC != osl::FileBase::E_None)
247  {
248  // Failed to open an existing log file.
249  mpLogFile.reset();
250  return;
251  }
252 
253  if (mpLogFile->setPos(osl_Pos_End, 0) != osl::FileBase::E_None)
254  {
255  // Failed to set the position to the end of the file.
256  mpLogFile.reset();
257  return;
258  }
259  }
260  else if (eRC != osl::FileBase::E_None)
261  {
262  // Failed to create a new file.
263  mpLogFile.reset();
264  return;
265  }
266 
267  // Output the header information.
268  writeAscii("---\n");
269  writeAscii("OpenCL: ");
270  writeAscii(ScCalcConfig::isOpenCLEnabled() ? "enabled\n" : "disabled\n");
271  writeAscii("---\n");
272 
273  sync();
274 }
275 
276 FormulaLogger::~FormulaLogger()
277 {
278  if (mpLogFile)
279  mpLogFile->close();
280 }
281 
282 void FormulaLogger::writeAscii( const char* s )
283 {
284  if (!mpLogFile)
285  return;
286 
287  sal_uInt64 nBytes;
288  mpLogFile->write(s, strlen(s), nBytes);
289 }
290 
291 void FormulaLogger::writeAscii( const char* s, size_t n )
292 {
293  if (!mpLogFile)
294  return;
295 
296  sal_uInt64 nBytes;
297  mpLogFile->write(s, n, nBytes);
298 }
299 
300 void FormulaLogger::write( const OUString& ou )
301 {
302  OString s = OUStringToOString(ou, RTL_TEXTENCODING_UTF8).getStr();
303  writeAscii(s.getStr(), s.getLength());
304 }
305 
306 void FormulaLogger::write( sal_Int32 n )
307 {
308  OString s = OString::number(n);
309  writeAscii(s.getStr(), s.getLength());
310 }
311 
312 void FormulaLogger::sync()
313 {
314  if (!mpLogFile)
315  return;
316 
317  mpLogFile->sync();
318 }
319 
320 void FormulaLogger::writeNestLevel()
321 {
322  // Write the nest level, but keep it only 1-character length to avoid
323  // messing up the spacing.
324  if (mnNestLevel < 10)
325  write(mnNestLevel);
326  else
327  writeAscii("!");
328 
329  writeAscii(": ");
330  for (sal_Int32 i = 1; i < mnNestLevel; ++i)
331  writeAscii(" ");
332 }
333 
334 FormulaLogger::GroupScope FormulaLogger::enterGroup(
335  const ScDocument& rDoc, const ScFormulaCell& rCell )
336 {
337  // Get the file name if available.
338  const SfxObjectShell* pShell = rDoc.GetDocumentShell();
339  const SfxMedium* pMedium = pShell ? pShell->GetMedium() : nullptr;
340  OUString aName;
341  if (pMedium)
342  aName = pMedium->GetURLObject().GetLastName();
343  if (aName.isEmpty())
344  aName = "-"; // unsaved document.
345 
346  OUString aGroupPrefix = aName + ": formula-group: " +
348 
349  bool bOutputEnabled = mpLastGroup != rCell.GetCellGroup().get();
350  mpLastGroup = rCell.GetCellGroup().get();
351 
352  return GroupScope(*this, aGroupPrefix, rDoc, rCell, bOutputEnabled);
353 }
354 
355 }
356 
357 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2116
URL aURL
static FormulaLogger get()
ScTokenArray * GetCode()
aBuf
OUString GetLastName(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
std::vector< OUString > maMessages
SC_DLLPUBLIC formula::FormulaGrammar::Grammar GetGrammar() const
Definition: document.hxx:982
SC_DLLPUBLIC formula::FormulaGrammar::AddressConvention GetAddressConvention() const
Definition: documen3.cxx:488
const ScFormulaCellGroupRef & GetCellGroup() const
SCTAB Tab() const
Definition: address.hxx:271
ScAddress aPos
SCROW GetSharedLength() const
OUString CreateString(sc::TokenStringContext &rCxt, const ScAddress &rPos) const
Create a string representation of formula token array without modifying the internal state of the tok...
Definition: token.cxx:5095
int i
Context for creating string from an array of formula tokens, used in ScTokenArray::CreateString().
GroupScope enterGroup(const ScDocument &, const ScFormulaCell &)
static const ScCalcConfig & GetGlobalConfig()
Definition: interpr4.cxx:3867
static bool isOpenCLEnabled()
Definition: calcconfig.cxx:69
const double * mpNumericArray
OString OUStringToOString(const OUString &str, ConnectionSettings const *settings)
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
void addMessage([[maybe_unused]] const OUString &)
rtl_uString ** mpStringArray
OUString aName
const INetURLObject & GetURLObject() const
Impl(FormulaLogger &rLogger, const OUString &rPrefix, const ScDocument &rDoc, const ScFormulaCell &rCell, bool bOutputEnabled)
sal_Int32 GetWeight() const
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1055
ScRefFlags
Definition: address.hxx:145
void addGroupSizeThresholdMessage(const ScFormulaCell &)
StackVar GetType() const
void addRefMessage(const ScAddress &, const ScAddress &, size_t, const formula::VectorRefArray &)
Dummy class with all empty inline methods.
bool SetSmartURL(OUString const &rTheAbsURIRef, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, FSysStyle eStyle=FSysStyle::Detect)
SfxMedium * GetMedium() const