desktop/inc/lib/init.hxx                  |    4 ++
 desktop/source/lib/init.cxx               |   55 ++++++++++++++++++++++++++++++
 include/LibreOfficeKit/LibreOfficeKit.h   |    4 ++
 include/LibreOfficeKit/LibreOfficeKit.hxx |   15 ++++++++
 include/sfx2/lokcallback.hxx              |    5 ++
 include/sfx2/lokhelper.hxx                |    4 ++
 include/sfx2/viewsh.hxx                   |    3 +
 include/test/lokcallback.hxx              |    2 +
 include/vcl/lok.hxx                       |    3 +
 sfx2/source/view/lokhelper.cxx            |   25 +++++++++++++
 sfx2/source/view/viewsh.cxx               |    7 +++
 vcl/source/app/svapp.cxx                  |   23 ++++++++++++
 12 files changed, 149 insertions(+), 1 deletion(-)

New commits:
commit a71a5cdb972174cb7c33e67927cd519152fd3cf6
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Wed Jul 27 14:02:48 2022 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Thu Jul 28 14:49:19 2022 +0200

    lok: add dumpState feature for better in-field diagnostics.
    
    Always suspicious that some un-expected dialog / state can cause
    strange behavior in a client. An initial cut at an API to make it
    easier to unwind such problems by exposing the toolkit state.
    
    Change-Id: If8f17943fa4837df4f9ca659a111dcdce5c23244
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137504
    Tested-by: Jenkins
    Reviewed-by: Michael Meeks <michael.me...@collabora.com>

diff --git a/desktop/inc/lib/init.hxx b/desktop/inc/lib/init.hxx
index c5dcea03d9fe..78c74c753063 100644
--- a/desktop/inc/lib/init.hxx
+++ b/desktop/inc/lib/init.hxx
@@ -21,6 +21,7 @@
 
 #include <osl/thread.h>
 #include <rtl/ref.hxx>
+#include <rtl/strbuf.hxx>
 #include <vcl/idle.hxx>
 #include <LibreOfficeKit/LibreOfficeKit.h>
 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
@@ -117,6 +118,7 @@ namespace desktop {
         virtual void libreOfficeKitViewInvalidateTilesCallback(const 
tools::Rectangle* pRect, int nPart) override;
         virtual void libreOfficeKitViewUpdatedCallback(int nType) override;
         virtual void libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int 
nViewId, int nSourceViewId) override;
+        virtual void dumpState(rtl::OStringBuffer &rState) override;
 
     private:
         struct CallbackData
@@ -261,6 +263,8 @@ namespace desktop {
         {
             return (mOptionalFeatures & feature) != 0;
         }
+
+        void dumpState(rtl::OStringBuffer &aState);
     };
 
     /// Helper function to extract the value from parameters delimited by
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index c0f129fef72f..74349ace09a4 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -1469,6 +1469,23 @@ void 
CallbackFlushHandler::libreOfficeKitViewUpdatedCallbackPerViewId(int nType,
     setUpdatedTypePerViewId(nType, nViewId, nSourceViewId, true);
 }
 
+void CallbackFlushHandler::dumpState(rtl::OStringBuffer &rState)
+{
+    // NB. no locking
+    rState.append("\nView:\t");
+    rState.append(static_cast<sal_Int32>(m_viewId));
+    rState.append("\n\tDisableCallbacks:\t");
+    rState.append(static_cast<sal_Int32>(m_nDisableCallbacks));
+    rState.append("\n\tStates:\n");
+    for (const auto &i : m_states)
+    {
+        rState.append("\n\t\t");
+        rState.append(static_cast<sal_Int32>(i.first));
+        rState.append("\t");
+        rState.append(i.second);
+    }
+}
+
 void CallbackFlushHandler::queue(const int type, const char* data)
 {
     CallbackData callbackData(data);
@@ -2337,6 +2354,8 @@ static void lo_sendDialogEvent(LibreOfficeKit* pThis,
 
 static void lo_setOption(LibreOfficeKit* pThis, const char* pOption, const 
char* pValue);
 
+static void lo_dumpState(LibreOfficeKit* pThis, const char* pOptions, char** 
pState);
+
 LibLibreOffice_Impl::LibLibreOffice_Impl()
     : m_pOfficeClass( gOfficeClass.lock() )
     , maThread(nullptr)
@@ -2363,6 +2382,7 @@ LibLibreOffice_Impl::LibLibreOffice_Impl()
         m_pOfficeClass->runLoop = lo_runLoop;
         m_pOfficeClass->sendDialogEvent = lo_sendDialogEvent;
         m_pOfficeClass->setOption = lo_setOption;
+        m_pOfficeClass->dumpState = lo_dumpState;
 
         gOfficeClass = m_pOfficeClass;
     }
@@ -4159,6 +4179,41 @@ static void lo_setOption(LibreOfficeKit* /*pThis*/, 
const char *pOption, const c
     }
 }
 
+static void lo_dumpState (LibreOfficeKit* pThis, const char* /* pOptions */, 
char** pState)
+{
+    if (!pState)
+        return;
+
+    // NB. no SolarMutexGuard since this may be caused in some extremis / 
deadlock
+    SetLastExceptionMsg();
+
+    *pState = nullptr;
+    OStringBuffer aState(4096*256);
+
+    LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+
+    pLib->dumpState(aState);
+
+    OString aStr = aState.makeStringAndClear();
+    *pState = strdup(aStr.getStr());
+}
+
+void LibLibreOffice_Impl::dumpState(rtl::OStringBuffer &rState)
+{
+    rState.append("LibreOfficeKit state:");
+    rState.append("\n\tLastExceptionMsg:\t");
+    rState.append(rtl::OUStringToOString(maLastExceptionMsg, 
RTL_TEXTENCODING_UTF8));
+    rState.append("\n\tUnipoll:\t");
+    rState.append(vcl::lok::isUnipoll() ? "yes" : "no: events on thread");
+    rState.append("\n\tOptionalFeatures:\t0x");
+    rState.append(static_cast<sal_Int64>(mOptionalFeatures), 16);
+    rState.append("\n\tCallbackData:\t0x");
+    rState.append(reinterpret_cast<sal_Int64>(mpCallback), 16);
+    // TODO: dump mInteractionMap
+    SfxLokHelper::dumpState(rState);
+    vcl::lok::dumpState(rState);
+}
+
 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* 
pCommand, const char* pArguments, bool bNotifyWhenFinished)
 {
     comphelper::ProfileZone aZone("doc_postUnoCommand");
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h 
b/include/LibreOfficeKit/LibreOfficeKit.h
index aff71dcc1f5d..3a706ba47091 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -118,6 +118,10 @@ struct _LibreOfficeKitClass
 
     /// @see lok::Office::setOption
     void (*setOption) (LibreOfficeKit* pThis, const char* pOption, const char* 
pValue);
+
+    /// @see lok::Document::dumpState
+    /// @since LibreOffice 7.5
+    void (*dumpState) (LibreOfficeKit* pThis, const char* pOptions, char** 
pState);
 };
 
 #define LIBREOFFICEKIT_DOCUMENT_HAS(pDoc,member) 
LIBREOFFICEKIT_HAS_MEMBER(LibreOfficeKitDocumentClass,member,(pDoc)->pClass->nSize)
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx 
b/include/LibreOfficeKit/LibreOfficeKit.hxx
index 6dbfbf964b29..4aafaa830626 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -1103,6 +1103,21 @@ public:
     {
         mpThis->pClass->setOption(mpThis, pOption, pValue);
     }
+
+    /**
+     * Debugging tool for triggering a dump of internal state.
+     *
+     * LibreOfficeKit can get into an unhelpful state at run-time when
+     * in heavy use. This provides a critical tool for inspecting
+     * relevant internal state.
+     *
+     * @param pOption future expansion - string options.
+     * @param pState - heap allocated, C string containing the state dump.
+     */
+    void dumpState(const char* pOption, char** pState)
+    {
+        mpThis->pClass->dumpState(mpThis, pOption, pState);
+    }
 };
 
 /// Factory method to create a lok::Office instance.
diff --git a/include/sfx2/lokcallback.hxx b/include/sfx2/lokcallback.hxx
index 32b6c08f865b..a92f60572145 100644
--- a/include/sfx2/lokcallback.hxx
+++ b/include/sfx2/lokcallback.hxx
@@ -11,6 +11,10 @@
 
 #include <sal/types.h>
 
+namespace rtl
+{
+class OStringBuffer;
+}
 namespace tools
 {
 class Rectangle;
@@ -46,6 +50,7 @@ public:
     virtual void libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int 
nViewId,
                                                             int nSourceViewId)
         = 0;
+    virtual void dumpState(rtl::OStringBuffer& rState) = 0;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx
index 4ac50a19cc69..0b18c0fa7b5e 100644
--- a/include/sfx2/lokhelper.hxx
+++ b/include/sfx2/lokhelper.hxx
@@ -17,6 +17,7 @@
 #include <sfx2/viewsh.hxx>
 #include <tools/gen.hxx>
 #include <cstddef>
+#include <rtl/strbuf.hxx>
 #include <rtl/string.hxx>
 #include <optional>
 #include <string_view>
@@ -147,6 +148,9 @@ public:
     /// This value is chosen such that sal_Int32 will not overflow when 
manipulated.
     static const tools::Long MaxTwips = 1e9;
 
+    /// Helper for diagnosing run-time problems
+    static void dumpState(rtl::OStringBuffer &rState);
+
 private:
     static int createView(SfxViewFrame* pViewFrame, ViewShellDocId docId);
 };
diff --git a/include/sfx2/viewsh.hxx b/include/sfx2/viewsh.hxx
index ea7d966c8cc7..3faf7aeb2071 100644
--- a/include/sfx2/viewsh.hxx
+++ b/include/sfx2/viewsh.hxx
@@ -55,6 +55,7 @@ class SfxPrinter;
 class NotifyEvent;
 class SfxInPlaceClient;
 class SfxLokCallbackInterface;
+namespace rtl { class OStringBuffer; }
 namespace vcl { class PrinterController; }
 
 namespace com::sun::star::awt{ class XPopupMenu; }
@@ -341,6 +342,8 @@ public:
 
     /// Set up a more efficient internal callback instead of 
LibreOfficeKitCallback.
     void setLibreOfficeKitViewCallback(SfxLokCallbackInterface* pCallback);
+    /// dump view state for diagnostics
+    void dumpLibreOfficeKitViewState(rtl::OStringBuffer &rState);
     /// Invokes the registered callback, if there are any.
     virtual void libreOfficeKitViewCallback(int nType, const char* pPayload) 
const override;
     virtual void libreOfficeKitViewCallbackWithViewId(int nType, const char* 
pPayload, int nViewId) const override;
diff --git a/include/test/lokcallback.hxx b/include/test/lokcallback.hxx
index a3f383bcec5e..e18724279040 100644
--- a/include/test/lokcallback.hxx
+++ b/include/test/lokcallback.hxx
@@ -39,6 +39,8 @@ public:
     virtual void libreOfficeKitViewUpdatedCallback(int nType) override;
     virtual void libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int 
nViewId,
                                                             int nSourceViewId) 
override;
+    virtual void dumpState(rtl::OStringBuffer&) override{};
+
     virtual void Invoke() override;
 
 private:
diff --git a/include/vcl/lok.hxx b/include/vcl/lok.hxx
index 108f46def8aa..31a4cf1ec494 100644
--- a/include/vcl/lok.hxx
+++ b/include/vcl/lok.hxx
@@ -12,6 +12,7 @@
 
 #include <sal/config.h>
 #include <vcl/dllapi.h>
+#include <rtl/strbuf.hxx>
 #include <LibreOfficeKit/LibreOfficeKitTypes.h>
 
 namespace vcl::lok
@@ -24,6 +25,8 @@ void VCL_DLLPUBLIC unregisterPollCallbacks();
 // Called to tell VCL that the number of document views has changed, so that 
VCL
 // can adjust e.g. sizes of bitmap caches to scale well with larger number of 
users.
 void VCL_DLLPUBLIC numberOfViewsChanged(int count);
+
+void VCL_DLLPUBLIC dumpState(rtl::OStringBuffer& rState);
 }
 
 #endif // INCLUDE_VCL_LOK_HXX
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
index 69cbc8b3d131..1d39838bfbf8 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -851,4 +851,29 @@ void SfxLokHelper::postMouseEventAsync(const 
VclPtr<vcl::Window> &xWindow, LokMo
     postEventAsync(pLOKEv);
 }
 
+void SfxLokHelper::dumpState(rtl::OStringBuffer &rState)
+{
+    SfxViewShell* pShell = SfxViewShell::Current();
+    sal_Int32 nDocId = pShell ? 
static_cast<sal_Int32>(pShell->GetDocId().get()) : -1;
+
+    rState.append("\n\tDocId:\t");
+    rState.append(nDocId);
+
+    if (nDocId < 0)
+        return;
+
+    rState.append("\n\tViewCount:\t");
+    rState.append(static_cast<sal_Int32>(getViewsCount(nDocId)));
+
+    const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
+    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+    while (pViewShell)
+    {
+        if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == 
pCurrentViewShell-> GetDocId())
+            pViewShell->dumpLibreOfficeKitViewState(rState);
+
+        pViewShell = SfxViewShell::GetNext(*pViewShell);
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx
index c14b31ce9862..d1f326c68b7f 100644
--- a/sfx2/source/view/viewsh.cxx
+++ b/sfx2/source/view/viewsh.cxx
@@ -1418,7 +1418,6 @@ bool SfxViewShell::ExecKey_Impl(const KeyEvent& aKey)
 
 void SfxViewShell::setLibreOfficeKitViewCallback(SfxLokCallbackInterface* 
pCallback)
 {
-    pImpl->m_pLibreOfficeKitViewCallback = nullptr;
     pImpl->m_pLibreOfficeKitViewCallback = pCallback;
 
     afterCallbackRegistered();
@@ -1436,6 +1435,12 @@ void 
SfxViewShell::setLibreOfficeKitViewCallback(SfxLokCallbackInterface* pCallb
     }
 }
 
+void SfxViewShell::dumpLibreOfficeKitViewState(rtl::OStringBuffer &rState)
+{
+    if (pImpl->m_pLibreOfficeKitViewCallback)
+        pImpl->m_pLibreOfficeKitViewCallback->dumpState(rState);
+}
+
 static bool ignoreLibreOfficeKitViewCallback(int nType, const 
SfxViewShell_Impl* pImpl)
 {
     if (!comphelper::LibreOfficeKit::isActive())
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index 36beccb6bcb5..0fc4a2b00e36 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -29,6 +29,7 @@
 #include <tools/debug.hxx>
 #include <tools/time.hxx>
 #include <tools/stream.hxx>
+#include <tools/json_writer.hxx>
 
 #include <unotools/configmgr.hxx>
 #include <unotools/resmgr.hxx>
@@ -1829,6 +1830,28 @@ void numberOfViewsChanged(int count)
     rCache.setMaxSize(count * 10);
 }
 
+void dumpState(rtl::OStringBuffer &rState)
+{
+    ImplSVData* pSVData = ImplGetSVData();
+    if (!pSVData)
+        return;
+
+    rState.append("\nWindows:\t");
+    rState.append(static_cast<sal_Int32>(Application::GetTopWindowCount()));
+
+    vcl::Window *pWin = Application::GetFirstTopLevelWindow();
+    while (pWin)
+    {
+        tools::JsonWriter props;
+        pWin->DumpAsPropertyTree(props);
+
+        rState.append("\n\tWindow: ");
+        rState.append(props.extractAsOString());
+
+        pWin = Application::GetNextTopLevelWindow( pWin );
+    }
+}
+
 } // namespace lok, namespace vcl
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to