Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jessoclarence/3038116 to your computer and use it in GitHub Desktop.
Save jessoclarence/3038116 to your computer and use it in GitHub Desktop.
Understanding LibreOffice Source Code
Aim
To make sense of LibreOffice source code. I plan to put a breakpoint in the constructors of
SwTableBox, and then get the back trace to see the flow and gain a little understanding of what's
going on. This is post is aimed at beginners, so a little knowledge of C++/Java should be able to
get one through this.
[A Little Background Info (Skip this)
I'm a programmer from the Center for Development of Advanced Computing (CDAC), India, currently
on a joint project with King Abdulaziz City for Science and Technology, Saudi Arabia, tasked with
fixing bugs/improving LibreOffice. This is part of my efforts to understand (and help my team-mates)
the source code. Any comments/suggestions/edits to this will be GREATLY APPRECIATED ;) ]
Initial Steps
I started to debug LibreOffice by following the steps listed in
https://wiki.documentfoundation.org/Development/How_to_debug after creating a build with
--enable-debug --enable-symbols set in autogen.sh
Starting the Debugger
I started soffice with ddd after sourcing ooenv. Next, I set a breakpoint in line 1717 and 1738,
constructors of SwTableBox in the file libo/sw/source/core/table/swtable.cxx, by typing
"break /home/jesso/Downloads/libreoffice/libo/sw/source/core/table/swtable.cxx:1717", at the
GDB console in the bottom of the DDD window, hoping these lines would be executed when a new table
was created, and clicked on Run to start the execution.
In Writer, I inserted a new table, and as expected, the breakpoint at 1717 was hit! Clicking on
Status -> Backtrace in ddd, opens a pop up window with the Backtrace. I am reproducing it below,
skipping the memory addresses alone.
Backtrace
#36 in main () at main.c:24
#35 in sal_main () at main.c:25
#34 in soffice_main () at sofficemain.cxx:77
#33 in SVMain () at svmain.cxx:210
#32 in ImplSVMain () at svmain.cxx:173
#31 in desktop::Desktop::Main () at app.cxx:1764
#30 in Application::Execute () at svapp.cxx:414
#29 in Application::Yield () at svapp.cxx:469
#28 in ImplYield () at svapp.cxx:435
#27 in GtkInstance::Yield () at gtkinst.cxx:538
#26 in GtkData::Yield () at gtkdata.cxx:578
#25 in g_main_context_iteration () from libglib-2.0.so.0
#24 in ?? () from libglib-2.0.so.0
#23 in g_main_context_dispatch () from libglib-2.0.so.0
#22 in ?? from libglib-2.0.so.0
#21 in call_userEventFn () at gtkdata.cxx:950
#20 in GtkData::userEventFn () at gtkdata.cxx:940
#19 in SalGenericDisplay::DispatchInternalEvent () at gendisp.cxx:102
#18 in SalFrame::CallCallback () at salframe.hxx:281
#17 in ImplWindowFrameProc () at winproc.cxx:2575
#16 in ImplHandleUserEvent () at winproc.cxx:2003
#15 in Link::Call () at link.hxx:143
#14 in SfxHintPoster::LinkStubDoEvent_Impl () at hintpost.cxx:65
#13 in SfxHintPoster::DoEvent_Impl () at hintpost.cxx:61
#12 in SfxHintPoster::Event () at hintpost.cxx:71
#11 in GenLink::Call () at genlink.hxx:45
#10 in Link::Call () at link.hxx:143
#09 in SfxDispatcher::LinkStubPostMsgHandler () at dispatch.cxx:1215
#08 in SfxDispatcher::PostMsgHandler () at dispatch.cxx:1244
#07 in SfxDispatcher::Call_impl () at dispatch.cxx:259
#06 in SfxShell::CallExec () at shell.hxx:199
#05 in SfxStubSwTextShellExecInsert () at swslots.hxx:2376
#04 in SwTextShell::ExecInsert () at textsh.cxx:466
#03 in SwBaseShell::InsertTable () at baseh.cxx:2654
#02 in SwEditShell::InsertTable () at edtab.cxx:77
#01 in SwDoc::InsertTable () at ndtbl.cxx:545
#00 in SwTableBox::SwTableBox () at swtable.cxx:1717
What happens in each call
I am skipping calls #36 - #25 for now as they are mostly related to application startup.
Calls #25 - #22 are inside glib (so, i'm not going to bother much)
#25 g_main_context_iteration () from libglib-2.0.so.0 - checks whether any event sources are
ready to be processed. More details can be found here at
http://developer.gnome.org/glib/2.31/glib-The-Main-Event-Loop.html#g-main-context-iteration
#23 in g_main_context_dispatch () from libglib-2.0.so.0 - dispatches all sources. More details
are at http://developer.gnome.org/glib/2.31/glib-The-Main-Event-Loop.html#g-main-context-dispatch
#21 in call_userEventFn () at gtkdata.cxx:950 - It is called by g_main_context_dispatch () with
a pointer to a generic pointer name data. I was unable to dereference it.
#20 in GtkData::userEventFn () at gtkdata.cxx:940 - The generic data pointer becomes a GtkData
pointer here. Displaying the value pointed to by the pointer reveals a SalGenericData object(?)
containing a SalData object (?) and a m_pUserEvent pointer. Dereferencing the m_pUserEvent pointer
I found it contained, pointers to callback_funcs, source_funcs, callback_data etc. All these
are interesting but I am unable to dereference these now as they are still generic pointer. Well,
on the GtkData pointer, GetGtkDisplay() method is called, which returns a GtkSalDisplay object, on
which the DispatchInternalEvent() method is called (the method is inherited from SalGenericDisplay).
Note - pThis, seems to be concerned with the data directly resulting from the event (?), pData seems
to contain the general details of the whole parent window (?), getting it from the GetGenericData()
call which returns the variable pImplSVData.
#19 in SalGenericDisplay::DispatchInternalEvent () at gendisp.cxx:102 - Here the lock is acquired,
to get the values from m_aUserEvents. So, this seems to be where the actual user event and the
corresponding data are fetched (?). The nEvent value is 22 (No idea why - is there any mapping to
integers and events?). The SalFrame is obtained and the CallCallback method is called on this object.
#18 in SalFrame::CallCallback () at salframe.hxx:281 - This just calls the function stored in its
own m_pProc variable.
#17 in ImplWindowFrameProc () at winproc.cxx:2575 - This contains the message loop. The nEvent = 22 is
SALEVENT_USEREVENT. Off we go to the next call in the same file.
#16 in ImplHandleUserEvent () at winproc.cxx:1986 - This just calls the function stored in the pointer.
#15 in Link::Call () at link.hxx:143 - The function pointed to by the pointer is called. Printing
pFunc we get (PSTUB) 0xb758ebbe <SfxHintPoster::LinkStubDoEvent_Impl(void*, void*)>
#14 in SfxHintPoster::LinkStubDoEvent_Impl () at /libo/sfx2/source/notify/hintpost.cxx:56 - Simply
passes the data along to the next function (?).
#13 in in SfxHintPoster::DoEvent_Impl () at /libo/sfx2/source/notify/hintpost.cxx:52 - It passes the
pointer it received on to the next function.
#12 SfxHintPoster::Event () at hintpost.cxx:60 - I guess now it comes directly here. Seems to
pass a function pointer deeper down (phew!).
#11 GenLink::Call () at /libo/sfx2/inc/sfx2/genlink.hxx:45 - Calls the function pointer.
#10 Link::Call () at /libo/solver/unxlngi6.pro/inc/tools/link.hxx:143 - We've come back (?) to Call #15.
It took the else part in the previous call, and we're back. But now we are calling a different function.
Printing pFunc here - (PSTUB) 0xb73d97ba <SfxDispatcher::LinkStubPostMsgHandler(void*, void*)>
I guess this Link::Call seems to be used to resolve function pointers in general (?).
#09 SfxDispatcher::LinkStubPostMsgHandler () at /libo/sfx2/source/control/dispatch.cxx:1205 -
This function is commented. (Yay!) Helper method to receive the asynchronously executed <SfxRequest>s.
#08 SfxDispatcher::PostMsgHandler () at /libo/sfx2/source/control/dispatch.cxx:1234 - Stuff is added
to SfxSlotServer and the next call is made. Expanding the pSlot pointer variable in ddd shows the
structure containing variables which have subsequent calls(SfxStubSwTextShellExecInsert) stored in them.
#07 in SfxDispatcher::Call_Impl () at /libo/sfx2/source/control/dispatch.cxx:249 - Its a helper
function to check whether a slot can be executed and check the execution itself (from the comments in
the code). It passes the target function SfxStubSwTextShellExecInsert to method CallExec in class SfxShell.
#06 in SfxShell::CallExec () at /libo/sfx2/inc/sfx2/shell.hxx:190 - It simply executes the function
pointed to by the function pointer.
#05 in SfxStubSwTextShellExecInsert () at /libo/workdir/unxlngi6.pro/SdiTarget/sw/sdi/swslots.hxx:2376,
now you'll find a line SFX_EXEC_STUB(SwTextShell,ExecInsert). I spent some time grepping for that, so
I'll reproduce that macro here. Btw, macro is defined in 'sfx2/inc/sfx2/msg.hxx'.
#define SFX_EXEC_STUB( aShellClass, aExecMethod) \
void SfxStub##aShellClass##aExecMethod( \
SfxShell *pShell, SfxRequest& rReq) \
{ \
(( aShellClass* ) pShell )->aExecMethod( rReq ); \
}
IMHO, this file seems to be a gold mine for putting break points, because, like winproc.cxx in #17,
all the control seems to flow through these two areas, but I'm not sure about putting breakpoints on
macros (?).
#04 in SwTextShell::ExecInsert () at /libo/sw/source/ui/shells/textsh.cxx:466 - The code is more
understandable at these layers. This function seems to be concerned with the insertion of *all* the
objects into the document (is the document itself called as the shell?). It has one huge switch case
for handling the various types of objects that can be inserted. Talking about that, the function gets
value of the slot from the SfxRequest variable that it received as an argument, whose value the ddd is
showing as 20330 (ie. variable nSlot=20330, of type unsigned short), that maps to FN_INSERT_TABLE.
How? These details are in /libo/clone/binfilter/binfilter/inc/bf_sw/cmdid.h, in line 349 we have
FN_INSERT + 30, and line 38 has FN_INSERT = (SID_SW_START + 300), and SID_SW_START = 20000 (this is
defined in /libo/binfilter/inc/bf_sfx2/sfxsids.hrc). So the question becomes, where did it calculate
20330 first (got to look keenly in the upper layers and update :( ). Inside the case, it simply calls
InsertTable function with the argument that it received.
#03 -> SwBaseShell::InsertTable () in libo/sw/source/ui/shells/baseh.cxx:2654 is where all the
options entered in the dialog are finally retrieved. The data is present in the _rRequest variable
and you can notice the retrieval of the table name, number of rows and columns etc. in lines
2582 - 2586, using SFX_REQUEST_ARG macros. The values that are finally stored in pName, pRows etc.
were allentered in the Table dialog box, and ddd displays the values as these are ordinary integers.
The items are then added once again as to _rRequest in proper Sfx formats(?) in lines 2641 to 2644.
Finally at 2654, the table is inserted! Notice that this call is sandwhiched between rSh.StartAllAction ()
and rSh.EndAllAction (). After this layer, the parameters are passed expicitly, that is, not
packaged in SfxRequest object anymore.
#02 -> SwEditShell::InsertTable () at libo/sw/source/core/edit/edtab.cxx:77 - Again all the contents of
the function are sandwiched between StartAllAction () and EndAllAction (). The current position of the
cursor is obtained from GetCrsr()->GetPoint(), and the GetDoc() function seems to return the current
document object, on which the next call happens.
#01 -> SwDoc::InsertTable () in libo/sw/source/core/docnode/ndtbl.cxx seems to deal at the
table level. It gets the boxes/cells from SwTableBox::SwTableBox () and stores it in the
variable pBox. It then constructs the table and returns a table node (?) - the variable
pNdTbl (of type SwTable).
#00 -> SwTableBox::SwTableBox () in libo/sw/source/core/table/swtable.cxx:1717 - This is where I put
the break point blindly, well all the constructors in this class had one. This constructor gets called
four times, that is, for each cell in the table (am I right about the cell->box mapping?).
The journey isn't over yet, the damned table hasn't even been printed to the screen, and every single
function has to return the data. I'll edit this again as I make my journey upwards all the way back!
Takeaways (Add anything that one can use later)
There seem to be a couple of important Get* functions like GetGenericData (), GetDoc (), GetCur () etc.
which can be used to get important objects.
pImplSVData - seems to be an important variable, used to get the display etc (?). Get it using
GetGenericData().
SwWrtShell Object
More details about that can be found here http://docs.libreoffice.org/sw/html/classSwWrtShell.html
It is used in many functions, and in each case is retrieved by using GetShell ().
SfxRequest Object
SfxRequest object seems to be a container through which the data are passed around generally.
* Items are added to it using, reqObj.AppendItem ( sfx_item ).
* After all items are appended method Done() is called on the object. ie. reqObj.Done()
* Items are retrieved using SFX_REQUEST_ARG macro.
- It is defined in libo/sfx2/inc/sfx2/request.hxx
- #define SFX_REQUEST_ARG(rReq, pItem, ItemType, nSlotId, bDeep) \
const ItemType *pItem = (const ItemType*)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment