This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.

System-Supplied Trace UDFs

Baya Pavliashvili and Kevin Kline

Most of you have probably created your own SQL Server user-defined functions (UDFs), but did you know that Microsoft ships a fair number of its own UDFs, especially in the most recent SP3? In this article, Baya Pavliashvili and Kevin Kline explore a subset of them that are related to traces. Some of you may want to (re)read previous SQL Server Professional articles about traditional UDFs such as Andrew Zanevsky's September 2000 column ("Granting Wishes with UDF"), Andrew Zanevsky and Anton Jiline's October 2001 article ("UDF Performance… or Lack of It"), or Jimmy Nilsson's July 2003 article ("Another UDF: Global Constants").

User-defined functions (UDFs) were a long-awaited addition to SQL Server 2000. UDFs, which are typically written by DBAs and developers, can be used to modularize T-SQL code—and sometimes even improve performance. In this article, however, we're going to zero in on a particular subset, the Microsoft-supplied "system" UDFs, which allow DBAs to manage traces.

Although the notion of system-supplied UDFs may seem like an oxymoron, SQL Server does ship with a number of built-in (read, "system-supplied") UDFs. And, although UDFs were a feature of SQL Server 2000 in its initial release, it's only in Service Pack 3 (SP3) that we see Microsoft making heavy use of them for its own purposes. All of Microsoft's system-supplied UDFs begin with "fn_" and reside in the master database.

Comparing system-supplied and standard UDFs

If you're familiar with UDFs, you probably know that UDFs can't modify data in permanent tables and are typically used to read data, make changes to data in table variables, and display data to the user. UDFs can, however, execute extended stored procedures, and system-supplied UDFs function similarly. In fact, most of Microsoft's system-supplied UDFs simply execute an extended stored procedure [XP's are DLLs that are typically written in C++. See Paul Storer-Martin's features on "Playing the ODS" in the July and August 2002 issues.—Ed.], so reading T-SQL UDF code of such functions won't do you much good. One thing that differentiates system-supplied UDFs is that they're executed with a slightly different syntax. Typical UDFs can be called as follows:
SELECT column_list 
FROM owner_name.UDF_name (@parameter1, … @parameterN)

System-supplied UDFs, however, take two colons (::) after the FROM keyword, and you don't have to specify the function owner:

SELECT column_list  
FROM  :: fn_SystemSuppliedUDF 
(@parameter1, … @parameterN)

For example, the fn_helpcollations() function returns the list of all collations supported by SQL Server 2000 and can be executed as follows:

SELECT * FROM :: fn_helpcollations()

UDFs used for tracing

A trace captures T-SQL statements sent to (and stored procedures executed on) a given instance of SQL Server and saves them to a .trc file. Traces can be set up through the Profiler or by executing the system stored procedure sp_trace_create, and you can specify any number of filtering criteria to limit the output. In this article, we'll drill down on trace-specific system-supplied UDFs.

fn_trace_gettable

fn_trace_gettable() accepts two parameters: the initial trace (.trc) filename to be read, and the number of files. When you create a trace, you can advise SQL Server to limit the trace file to a particular size. When the trace file reaches this limit, SQL Server will automatically create a new "rollover" file. The second parameter of fn_trace_gettable specifies the number of rollover files to be read, starting at the file specified by the first parameter.

If you prefer to have trace information stored in a database, you could use fn_trace_gettable to save the trace file as a table by running a query similar to this:

SELECT * 
INTO dbo.my_trace_table
FROM :: fn_trace_gettable
('c:\trace_file.trc', default)

However, it's pretty easy to query the raw data directly, searching for some meaningful string. In our environment, all user-defined stored procs have the "usp" prefix, so we can run a query like this to determine their duration:

SELECT TextData, duration 
FROM :: 
  fn_trace_gettable('c:\trace_file.trc', default)
WHERE TextData LIKE '%usp%'
AND duration > 3000

Armed with a list of long queries, we might want to refine the SELECT to determine whether these queries are executing slowly each time or only during the peak user activity.

fn_trace_getinfo

This function lets you get high-level information about a particular trace or all active traces that are executing against a particular instance of SQL Server. This function accepts a single parameter—trace ID.

To limit the information about a particular trace, you must specify the trace identifier. Alternatively, you can specify DEFAULT, or "0", as the trace identifier to get information about all active traces. SQL Server assigns the trace identifier to each trace at creation; if you don't know which trace you're looking for, simply run this function specifying "0" as the parameter—then you can limit the output to the trace that interests you the most. The output of fn_trace_getinfo is described in Table 1.

Table 1. Output of fn_trace_getinfo.

Column name

Meaning

TraceID

Internal trace identifier.Can be used to manage traces through system stored procedures sp_trace_**.

Property

Trace properties, including:
1 — trace options (discussed shortly)
2 — filename
3 — max size for trace file
4 — stop time
5 — current status of the trace

Value

Value of each property returned.

Trace options can be specified through the sp_trace_create system procedure (see Table 2).

Table 2. Trace options you can use during sp_trace_create.

Option name

Value

Meaning

Trace_produce_rowset

1

Trace will produce a rowset.

Trace_file_rollover

2

Trace will create a new file when the first file will reach a maximum size specified at creation. SQL Server will append numbers to each file (1, 2, 3, and so on) in sequence.

Shutdown_on_error

4

Advises to shut down SQL Server if trace can't be written to the file.

Trace_produce_blackbox

8

If this option is chosen, SQL Server will save the last 5MB of trace output.

Let's take a look at an example to see how fn_trace_getinfo works. Suppose we create a trace using the following query:

/* declare a variable to hold trace ID */
DECLARE @trace_id INT
/* create the trace */
EXEC sp_trace_create      
  @traceid = @trace_id  OUTPUT, 
       @options =  2  ,
  @tracefile =  N'e:\trace_file.trc' , 
  @maxfilesize = 5, 
  @stoptime = NULL

/* start the trace we just created. 
by default the trace is stopped at creation
*/
EXEC sp_trace_setstatus @trace_id, 1
/* return the trace identifier*/
SELECT 'trace ID is: ' + CAST(@trace_id AS VARCHAR(4))

--Result: 
-------------------------
trace ID is: 2

Now we can use fn_trace_getinfo to get information about this particular trace:

SELECT * FROM :: fn_trace_getinfo(2)

The results are shown in Table 3.

Table 3. Sample results of using fn_trace_getinfo.

traceID

Property

Value

2

1

2

2

2

e:\trace_file.trc

2

3

5

2

4

NULL

2

5

1

This output tells us that we have an active trace that can grow up to 5MB before rolling over to another file. There's no stop time (property = 4) specified for this trace, so it will run until SQL Server is stopped or the trace is stopped using a sp_trace_setstatus system procedure.

fn_trace_getfilterinfo and fn_trace_geteventinfo

These functions let you retrieve the metadata of a particular trace. (The output of these functions is rather cryptic if you aren't familiar with the identifiers of events that you can trace and the filter column identifiers; see the Books Online topic "sp_trace_setevent.")

Both of these functions take trace identifier as their only parameter. fn_trace_getfilterinfo returns the filter(s) applied to the specified trace. For instance, suppose we limit our trace to the pubs database, because we're trying to troubleshoot a long-running query in that database. We can execute the function as follows:

SELECT * FROM :: fn_trace_getfilterinfo(1) 

--Results: 
columnID   logical_operator comparison_operator value    
---------- ---------------- ------------------- ---------
35                0         6                   pubs

This output tells us that the trace we specified has a filter defined on column 35 (database name) and that the logical operator ("AND" or "OR") isn't used (0) since there's only one condition. The comparison operator is "LIKE" (= 6), and the value of filter is "pubs".

Depending on the type of filter you apply, your trace comparison operator can vary. In most cases, you'll use LIKE or NOT LIKE. However, if you're limiting the trace to a particular process or to a particular database ID, then you can specify "=", ">", "< >", "<", "> =", or "< =".

Note: If you set up a trace using SQL Server Profiler, the trace will automatically add a filter excluding the statements sent to SQL Server by the Profiler itself. If you'd like to spy on the Profiler, simply turn this filter off.

fn_tracegeteventinfo provides information about the events collected by a particular trace. There are numerous events you can specify within the trace. The more events you trace, the more information you'll have to plow through to digest, so be careful. We recommend learning the meaning of each event and only choosing those that matter most for your particular troubleshooting effort. fn_tracegeteventinfo is particularly useful when you're simultaneously running multiple traces for different purposes.

To get event identifiers included in our trace, we can execute fn_tracegeteventinfo as follows:

SELECT DISTINCT eventid 
FROM :: fn_trace_geteventinfo(1)

--Results: 

eventid     
----------- 
12
37
40
41
42
43

This tells us that we're examining the following events:

  • SQL: BatchCompleted—event id of 12
  • SP: Recompile—event id of 37
  • SQL: StatementStarting—event id of 40
  • SQL: StatementCompleted—event id of 41
  • SP: Starting—event id of 42
  • SP: Completed—event id of 43

Similarly, we could execute the same function with a slight change to get all the data columns that the trace is collecting with this statement:

SELECT DISTINCT columnid FROM :: fn_trace_geteventinfo(1)

--Results: 

columnid    
----------- 
1
10
11
12
13
14
16
17
18

Here, we're collecting typical data useful for tuning: text data, application name, SQL Server login, SPID, duration, start time and end time, reads, writes, and CPU utilized for query.

Using system-supplied UDFs

Now that you know a bit about system-supplied UDFs, you can use them in your own UDFs. One of the major limitations of UDFs is that they can't call stored procedures. UDFs can, however, call other UDFs. The following UDF utilizes fn_trace_geteventinfo to make the results more readable:
CREATE FUNCTION dbo.fn_GetTraceColumns (@trace_id INT)
RETURNS @TraceColumns TABLE (
  column_id INT, 
  column_name VARCHAR(155)
)
AS
BEGIN
INSERT @TraceColumns (
column_id)
SELECT DISTINCT columnid FROM :: 
fn_trace_geteventinfo(@trace_id)
UPDATE @TraceColumns 
SET column_name = 
  CASE column_id 
  WHEN 1 THEN 'TextData' 
  WHEN 3 THEN 'DatabaseID'  
  WHEN 4 THEN 'TransactionID' 
  WHEN 6 THEN 'NTUserName' 
  -- similar statements omitted here - see Source 
  ELSE 'other'
  END
RETURN
END

This function can be executed as follows:

SELECT * FROM dbo.fn_getTraceColumns(3)

Summary

In this article, we've introduced you to some powerful system-supplied UDFs available in SQL Server 2000. We hope you enjoyed it and will explore the other system-supplied UDFs.

Download TRACEUDFS.SQL

Sidebar: Additional Resources

  • 284790 INF: How to Create a SQL Server 2000 Trace
  • 283786 INF: How to Monitor SQL Server 2000 Traces
  • 270599 BUG: fn_trace_gettable Function Cannot Read Rollover Files 273972 Generated by SQL
  • ProfilerHOW TO: Programmatically Load Trace Files into Tables
  • 268591 PRB: ODBC Tracing to SQL.LOG Can Slow SQL Server or Consume All Disk Space
  • 307786 INF: Tracing to Network Drive May Reduce SQL Server Throughput
  • 286239 BUG: Replay Tool Uses LoginName Column for SETUSER Instead of DatabaseUserName Column
  • Andrew Novick's "Find Out What They're Doing with fn_get_sql"—www.databasejournal.com/features/mssql/article.php/2189761

—kw

To find out more about SQL Server Professionaland Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

This article is reproduced from the August 2003 issue of Microsoft SQL Server Professional. Copyright 2003, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. SQL Server Professionalis an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

© 2009 Microsoft Corporation. All rights reserved.   Terms of Use | Trademarks | Privacy Statement
Page view tracker
Rate the Lightweight library
x
Lightweight builds on ScriptFree (loband) by adding features you've requested: a SearchBox and default code language selection.
Do you like the SearchBox?
Do you like the tabbed code blocks?
How useful is this topic?
Tell us more.
Thanks
x
You're helping to improve MSDN Online.
Feedback
Switch View
Classic
Lightweight Beta
ScriptFree
Switch View