We've already seen how BMC_DEBUG makes PL/SQL monitoring available via DBMS_APPLICATION_INFO.
However, there are times when this functionality is not desirable. If you already run an application which makes use of the V$SESSION MODULE, ACTION and CLIENT_INFO columns in order to store session state information, you would not want instrumented code to overwrite those values.
BMC_DEBUG provides an alternative way of monitoring the activity of an instrumented procedure, which is via a global context. A context is an in-memory list of key/value pairs, and a global context can be written to by one session and read by another.
Using a context to monitor the current state of a running procedure has a couple of advantages over using a query on V$SESSION :-
It doesn't require the monitoring user to have privilege on V$SESSION, which may be restricted in a production environment
It can store larger values, as V$SESSION.CLIENT_INFO is limited to 64 characters, but a context can hold values up to 4K
User code can store its own key/value pairs, so application specific values can be monitored along with those provided by BMC_DEBUG
In order to use context-based monitoring, a global context needs to be created which trusts the BMC_DEBUG package. For example, if BMC_DEBUG was installed in the DBG_OWNER schema, the context would be created as :-
SQL> CREATE CONTEXT BMC_DEBUG_CTX USING DBG_OWNER.BMC_DEBUG ACCESSED GLOBALLY;
Context created.
Contexts are non-schema objects, so it doesn't matter which user creates the context, but the user will need to have the CREATE ANY CONTEXT privilege to run the statement.
Also, the name of the context can be changed if necessary, as BMC_DEBUG finds the name of the context to use from a global parameter.
Global Parameter Settings
There are several global parameter settings which affect the way in which contexts are used by BMC_DEBUG. The most significant ones are :-
GLOBAL CONTEXT NAME - This specifies the name of the global context which trusts this version of the BMC_DEBUG package. The default name is BMC_DEBUG_CTX, but it can be changed if the context is created with a different name.
WRITE CONTEXT INFO - This is an integer which controls which information is written to the context. Its first 4 bits represent switches which control the writing of four sets of information :-
1 = Write the package/procedure names to the context attributes PACKAGE and PROCEDURE
2 = Write messages passed to BMC_DEBUG.MSG to the context attribute MSG
4 = Write the CALL_INFO value passed to BMC_DEBUG.BEGINCALL to the context attribute CALL_INFO
8 = Allow user attribute values to be written to the context
So the value of this global parameter should be the sum of the options you wish to enable. For example, if you wanted the package and procedure names, and the CALL_INFO details written to the global context, you would set the global parameter value of WRITE CONTEXT INFO to 5 (which is 1+4).
Setting these two parameters to appropriate values will cause BMC_DEBUG to start writing values to the global context. There is one more global parameter which needs to be considered, as its setting has implications for monitoring and memory usage.
CONTEXT ATTRIBUTES PERSIST is a global parameter which controls how long attribute values exist in the global context before being removed. The default value is FALSE, which means that when the top-level instrumented procedure ends, the attributes which it and its child calls wrote to the global context are removed, so will not be queryable once the procedure has ended.
If this global parameter is set to TRUE, the attribute values will not be removed, so will be queryable after the procedure which wrote them has ended, but this also means that they could potentially exist in the context forever (or until the instance restarts) as they are not linked to the session which created them, so will still exist after the session ends.
If you set this parameter to TRUE, you may want to create a BEFORE LOGOFF trigger for each user which runs instrumented code, and the trigger should call BMC_DEBUG.CLEAR_SESSION_CONTEXT_VALUES to delete the context values which BMC_DEBUG created throughout the life of the session.
Querying The Context
Once you have created the context and set the global parameter values accordingly, BMC_DEBUG will write attribute values to it as instrumented procedures are run.
To demonstrate this, we will use two separate SQL*Plus sessions, and will run some BMC_DEBUG procedures interactively in those sessions.
In our first session, we will invoke BMC_DEBUG.BEGINCALL to simulate the start of a procedure :-
SESSION 1 SQL> exec BMC_DEBUG.BEGINCALL('PROCEDURE','CONTEXT_DEMO');
PL/SQL procedure successfully completed.
SESSION 1 SQL> SELECT bmc_debug.get_oracle_session_id FROM dual;
GET_ORACLE_SESSION_ID
---------------------
132
So we've called BEGINCALL to setup the call stack and write some values to the context, and we have found the session ID (SID) of the session, which we will need to query the context.
Now in our second session, we can interrogate the context attribute values :-
SESSION 2 SQL> exec dbms_session.set_identifier('132');
PL/SQL procedure successfully completed.
SESSION 2 SQL> SELECT sys_context('BMC_DEBUG_CTX','PACKAGE'), sys_context('BMC_DEBUG_CTX','PROCEDURE') FROM dual;
SYS_CONTEXT('BMC_DEBUG_CTX','PACKAGE') SYS_CONTEXT('BMC_DEBUG_CTX','PROCEDURE')
----------------------------------------- -----------------------------------------------------------------------
PROCEDURE CONTEXT_DEMO
(NOTE - If you are using a RAC database, the instance you are connected to when querying the context must be the same instance as the session whose details you wish to query :)
So here we have set the session identifier to the session ID (SID) of the session we want to query, then we have used SYS_CONTEXT to query two context attributes called PACKAGE and PROCEDURE, into which BMC_DEBUG.BEGINCALL has written the names of the package and procedure which were passed to it.
Storing User Defined Attributes
It is possible for user code to create its own attributes in the global context and write values to them which can be monitored from another database session.
For these values to be written, the value of the global parameter WRITE CONTEXT INFO must be >= 8.
Here is an example of user code writing a user context attribute :-
bmc_debug.set_user_context('QUERY_STRING','keyword=malaria');
Just as with the context attributes written by BMC_DEBUG, to query user attributes in another session, you would set the session identifier to the SID of the session to monitor, and use SYS_CONTEXT to retrieve the attribute value.
Security
An issue which may cause concern when using a global context for monitoring is security. As there are no database level privileges regarding contexts, once a context has been created, any database user will be able to query the values it contains. To query it they would need to know :-
The SID of the session whose values they want to query
The name of the global context
The name of the attributes whose values they want to query
However, some of these values could be gained from documentation (such as the default global context name, or the attribute names written by BMC_DEBUG) and other could be guessed (such as the SID of a session running instrumented code).
If you wanted to make the querying of global context values more secure, there are two global parameters which can be used to do this.
CONTEXT SESSION PREFIX can be used to set a string which is used to prefix the value of the session identifier. By default when BMC_DEBUG writes values to the global context, it uses the session ID (SID) of the executing procedure's session as the session identifier, and this is the value which the querying session needs to pass to DBMS_SESSION.SET_IDENTIFIER to point at the context entries it wants to query.
If a value is set for CONTEXT SESSION PREFIX, that value will prefix the SID when it is used as the context session identifier. For example, if I configured the value of the CONTEXT SESSION PREFIX global parameter to 'ZQV:', in order to query the context values for session 1378, I would need to do this in another session :-
SESSION 2 SQL> exec dbms_session.set_identifier('ZQV:1378');
PL/SQL procedure successfully completed.
As this value will be held in the BMC_DEBUG_GLOBAL_PARAMETER table, on which regular users should not have SELECT privilege, and is not displayed by BMC_DEBUG.DUMP_GLOBALS, it should not be possible for an unprivileged user to find this value, which will make it much more difficult for them to query the global context.
Another global parameter which can be used to increase context security is CONTEXT MONITOR USERNAME. By default this is NULL, which means that any user can query the global context. If this value of this parameter is set to 'USER', the context values can only be queried by database sessions which are connected as the same user which wrote the values. For example, if instrumented code running as user FRED wrote some values to the global context, they could only be queried by another session which was logged in as FRED.
If the value of this parameter is set to a username, only sessions which are logged in as that user will be able to query the global context. For example, if the value of CONTEXT MONITOR USERNAME was set to 'ALICE', only sessions logged in as ALICE could query the global context values, irrespective of which users wrote the values to the context.
If you use CONTEXT MONITOR USERNAME to restrict context access, the sessions which query the context will still need to call DBMS_SESSION.SET_IDENTIFIER to set the identifier of the session they want to query, which could include a CONTEXT SESSION PREFIX value.