WinDBG+SOS: Getting at the values in a DataTable

by: Johan Olofsson

I have been doing some memorydump debugging using WinDBG and SOS lately, and at one time I needed to dump all values for a column in a DataTable.

I started by executing !do on the address of the DataTable (in my case 0x0a8cdaa8), and this gave me the dump  below:

   1: 0:025> !do 0x0a8cdaa8 
   2: Name: System.Data.DataTable
   3: MethodTable 0x175ab588
   4: EEClass 0x175c0074
   5: Size 236(0xec) bytes
   6: GC Generation: 2
   7: mdToken: 0x0200003d  (c:\windows\assembly\gac\\1.0.5000.0__b77a5c561934e089\
   8: FieldDesc*: 0x175aa6b4
   9:         MT      Field     Offset                 Type       Attr      Value Name
  10: 0x17387bf4 0x4000583      0x4                CLASS   instance 0x00000000 site
  11: 0x17387bf4 0x4000584      0x8                CLASS   instance 0x00000000 events
  12: 0x17387bf4 0x4000582        0                CLASS     shared   static EventDisposed
  13:     >> Domain:Value 0x000c53e8:NotInit  0x00123418:0x02a5ef58 <<
  14: 0x175ab588 0x40003ee      0xc                CLASS   instance 0x00000000 dataSet
  15: 0x175ab588 0x40003ef     0x10                CLASS   instance 0x00000000 defaultView
  16: 0x175ab588 0x40003f0     0xa8         System.Int32   instance 17699 nextRowID
  17: 0x175ab588 0x40003f1     0x14                CLASS   instance 0x0a8cdd28 rowCollection
  18: 0x175ab588 0x40003f2     0x18                CLASS   instance 0x0a8cdefc columnCollection
  19: 0x175ab588 0x40003f3     0x1c                CLASS   instance 0x0a8cdd54 constraintCollection
  20: 0x175ab588 0x40003f4     0xac         System.Int32   instance 1 elementColumnCount
  21: 0x175ab588 0x40003f5     0x20                CLASS   instance 0x0a8cdddc parentRelationsCollection
  22: 0x175ab588 0x40003f6     0x24                CLASS   instance 0x0a8cde6c childRelationsCollection
  23: 0x175ab588 0x40003f7     0x28                CLASS   instance 0x0a8cdca0 recordManager
  24: 0x175ab588 0x40003f8     0x2c                CLASS   instance 0x0a8ce068 indexes
  25: 0x175ab588 0x40003f9     0x30                CLASS   instance 0x00000000 shadowIndexes
  26: 0x175ab588 0x40003fa     0xb0         System.Int32   instance 0 shadowCount
  27: 0x175ab588 0x40003fb     0x34                CLASS   instance 0x00000000 extendedProperties
  28: 0x175ab588 0x40003fc     0x38                CLASS   instance 0x0e460224 tableName
  29: 0x175ab588 0x40003fd     0x3c                CLASS   instance 0x00000000 tableNamespace
  30: 0x175ab588 0x40003fe     0x40                CLASS   instance 0x0e460224 tablePrefix
  31: 0x175ab588 0x40003ff     0xb8       System.Boolean   instance 0 caseSensitive
  32: 0x175ab588 0x4000400     0xb9       System.Boolean   instance 1 caseSensitiveAmbient
  33: 0x175ab588 0x4000401     0x44                CLASS   instance 0x00000000 culture
  34: 0x175ab588 0x4000402     0x48                CLASS   instance 0x00000000 displayExpression
  35: 0x175ab588 0x4000403     0x4c                CLASS   instance 0x0a47ae64 compareInfo
  36: 0x175ab588 0x4000404     0xb4         System.Int32   instance 25 compareFlags
  37: 0x175ab588 0x4000405     0xba       System.Boolean   instance 1 fNestedInDataset
  38: 0x175ab588 0x4000406     0x50                CLASS   instance 0x00000000 encodedTableName
  39: 0x175ab588 0x4000407     0x54                CLASS   instance 0x00000000 xmlText
  40: 0x175ab588 0x4000408     0x58                CLASS   instance 0x00000000 _colUnique
  41: 0x175ab588 0x4000409     0xbb       System.Boolean   instance 0 textOnly
  42: 0x175ab588 0x400040a     0xc8            VALUETYPE   instance start at 0a8cdb70 minOccurs
  43: 0x175ab588 0x400040b     0xd8            VALUETYPE   instance start at 0a8cdb80 maxOccurs
  44: 0x175ab588 0x400040c     0xbc       System.Boolean   instance 0 repeatableElement
  45: 0x175ab588 0x400040d     0x5c                CLASS   instance 0x0a490010 typeName
  46: 0x175ab588 0x4000410     0x60                CLASS   instance 0x00000000 primaryKey
  47: 0x175ab588 0x4000411     0x64                CLASS   instance 0x02a6b340 primaryIndex
  48: 0x175ab588 0x4000412     0x68                CLASS   instance 0x00000000 delayedSetPrimaryKey
  49: 0x175ab588 0x4000413     0x6c                CLASS   instance 0x00000000 saveIndexes
  50: 0x175ab588 0x4000414     0x70                CLASS   instance 0x00000000 loadIndex
  51: 0x175ab588 0x4000415     0xbd       System.Boolean   instance 0 savedEnforceConstraints
  52: 0x175ab588 0x4000416     0xbe       System.Boolean   instance 0 inDataLoad
  53: 0x175ab588 0x4000417     0xbf       System.Boolean   instance 1 initialLoad
  54: 0x175ab588 0x4000418     0xc0       System.Boolean   instance 0 schemaLoading
  55: 0x175ab588 0x4000419     0xc1       System.Boolean   instance 1 fComputedColumns
  56: 0x175ab588 0x400041a     0xc2       System.Boolean   instance 1 enforceConstraints
  57: 0x175ab588 0x400041b     0x74                CLASS   instance 0x00000000 propertyDescriptorCollectionCache
  58: 0x175ab588 0x400041c     0xc3       System.Boolean   instance 0 fInitInProgress
  59: 0x175ab588 0x400041d     0x78                CLASS   instance 0x00000000 _nestedParentRelation
  60: 0x175ab588 0x400041e     0xc4       System.Boolean   instance 0 mergingData
  61: 0x175ab588 0x400041f     0x7c                CLASS   instance 0x00000000 onRowChangedDelegate
  62: 0x175ab588 0x4000420     0x80                CLASS   instance 0x00000000 onRowChangingDelegate
  63: 0x175ab588 0x4000421     0x84                CLASS   instance 0x00000000 onRowDeletingDelegate
  64: 0x175ab588 0x4000422     0x88                CLASS   instance 0x00000000 onRowDeletedDelegate
  65: 0x175ab588 0x4000423     0x8c                CLASS   instance 0x00000000 onColumnChangedDelegate
  66: 0x175ab588 0x4000424     0x90                CLASS   instance 0x00000000 onColumnChangingDelegate
  67: 0x175ab588 0x4000425     0x94                CLASS   instance 0x00000000 onPropertyChangingDelegate
  68: 0x175ab588 0x4000426     0x98                CLASS   instance 0x0a8ce0d0 rowBuilder
  69: 0x175ab588 0x4000427     0x9c                CLASS   instance 0x0a8cdb94 delayedViews
  70: 0x175ab588 0x4000428     0xa0                CLASS   instance 0x0a8cdbfc dvListeners
  71: 0x175ab588 0x4000429     0xa4                CLASS   instance 0x0a8cdc64 indexesLock
  72: 0x175ab588 0x400040e        0                CLASS     shared   static zeroIntegers
  73:     >> Domain:Value 0x000c53e8:NotInit  0x00123418:0x02a6b340 <<
  74: 0x175ab588 0x400040f      0x4                CLASS     shared   static zeroColumns
  75:     >> Domain:Value 0x000c53e8:NotInit  0x00123418:0x02a6b34c <<


Then, I ran !do on the rowCollection field which resulted in this list:

   1: 0:025> !do 0x0a8cdd28 
   2: Name: System.Data.DataRowCollection
   3: MethodTable 0x175aba6c
   4: EEClass 0x175c00d8
   5: Size 20(0x14) bytes
   6: GC Generation: 2
   7: mdToken: 0x02000035  (c:\windows\assembly\gac\\1.0.5000.0__b77a5c561934e089\
   8: FieldDesc*: 0x175ab8fc
   9:         MT      Field     Offset                 Type       Attr      Value Name
  10: 0x175aa2a8 0x4000331        0                CLASS     shared   static RefreshEventArgs
  11:     >> Domain:Value 0x000c53e8:NotInit  0x00123418:0x02a5efa8 <<
  12: 0x175aba6c 0x40003bf      0x4                CLASS   instance 0x0a8cdaa8 table
  13: 0x175aba6c 0x40003c0      0x8                CLASS   instance 0x0a8cdd3c list
  14: 0x175aba6c 0x40003c1      0xc         System.Int32   instance 0 nullInList


Then by executing !do on the "list" field I got to an arraylist:

   1: 0:025> !do 0x0a8cdd3c 
   2: Name: System.Collections.ArrayList
   3: MethodTable 0x0226fcb4
   4: EEClass 0x022e4fb8
   5: Size 24(0x18) bytes
   6: GC Generation: 2
   7: mdToken: 0x02000100  (c:\windows\\framework\v1.1.4322\mscorlib.dll)
   8: FieldDesc*: 0x0226f8e8
   9:         MT      Field     Offset                 Type       Attr      Value Name
  10: 0x0226fcb4 0x4000362      0x4                CLASS   instance 0x14070700 _items
  11: 0x0226fcb4 0x4000363      0xc         System.Int32   instance 17698 _size
  12: 0x0226fcb4 0x4000364     0x10         System.Int32   instance 17699 _version
  13: 0x0226fcb4 0x4000365      0x8                CLASS   instance 0x00000000 _syncRoot


To iterate over an array of objects, I hacked up a small WinDBG-script, see below. (I have saved this script as textfile named "dumparray" in the same folder as WinDBG.exe).

   1: .foreach ( o { !do ${$arg1} -v -short }) { !do ${o} }

Now I can execute the command: $$>a< dumparray 0x14070700 to get a dump of all DataRow objects in the DataRowCollection. (The address 0x14070700 is simply what the _items field contains)

This gave me a list of dumps of all the DataRow objects, of which I show one below:

   1: Name: System.Data.DataRow
   2: MethodTable 0x175ac0cc
   3: EEClass 0x175c013c
   4: Size 40(0x28) bytes
   5: GC Generation: 2
   6: mdToken: 0x02000030  (c:\windows\assembly\gac\\1.0.5000.0__b77a5c561934e089\
   7: FieldDesc*: 0x175abb18
   8:         MT      Field     Offset                 Type       Attr      Value Name
   9: 0x175ac0cc 0x40003a8     0x10         System.Int32   instance 328 oldRecord
  10: 0x175ac0cc 0x40003a9     0x14         System.Int32   instance 328 newRecord
  11: 0x175ac0cc 0x40003aa     0x18         System.Int32   instance -1 tempRecord
  12: 0x175ac0cc 0x40003ab     0x1c         System.Int32   instance 329 rowID
  13: 0x175ac0cc 0x40003ac     0x20       System.Boolean   instance 0 inChangingEvent
  14: 0x175ac0cc 0x40003ad     0x21       System.Boolean   instance 0 inDeletingEvent
  15: 0x175ac0cc 0x40003ae     0x22       System.Boolean   instance 0 inCascade
  16: 0x175ac0cc 0x40003af      0x4                CLASS   instance 0x00000000 error
  17: 0x175ac0cc 0x40003b0      0x8                CLASS   instance 0x00000000 _element
  18: 0x175ac0cc 0x40003b1      0xc                CLASS   instance 0x0a8cdaa8 _Table
  19: 0x175ac0cc 0x40003b2        0                CLASS     shared   static zeroColumns
  20:     >> Domain:Value 0x000c53e8:NotInit  0x00123418:0x02a6b824 <<


Ok, now that we have dumped the DataRow, where's the values?? We can see the row index  in the "oldRecord" field, but what I thouht would contain the actual data, the field "element" is always null..?

A little investigation turns out that the data is actually stored in the DataColumn on  field named "storage". So, to get at our values, we need to back up to the DataTable and instead dump the columnColletion (0x0a8cdefc) which gives the below dump:

   1: 0:025> !do 0x0a8cdefc 
   2: Name: System.Data.DataColumnCollection
   3: MethodTable 0x1768fdcc
   4: EEClass 0x17696bac
   5: Size 52(0x34) bytes
   6: GC Generation: 2
   7: mdToken: 0x0200001a  (c:\windows\assembly\gac\\1.0.5000.0__b77a5c561934e089\
   8: FieldDesc*: 0x1768fa0c
   9:         MT      Field     Offset                 Type       Attr      Value Name
  10: 0x175aa2a8 0x4000331        0                CLASS     shared   static RefreshEventArgs
  11:     >> Domain:Value 0x000c53e8:NotInit  0x00123418:0x02a5efa8 <<
  12: 0x1768fdcc 0x4000376      0x4                CLASS   instance 0x0a8cdaa8 table
  13: 0x1768fdcc 0x4000377      0x8                CLASS   instance 0x0a8cdf30 list
  14: 0x1768fdcc 0x4000378     0x28         System.Int32   instance 1 defaultNameIndex
  15: 0x1768fdcc 0x4000379      0xc                CLASS   instance 0x00000000 delayedAddRangeColumns
  16: 0x1768fdcc 0x400037a     0x10                CLASS   instance 0x0a8ce18c columnQueue
  17: 0x1768fdcc 0x400037b     0x14                CLASS   instance 0x0a8cdf98 columnFromName
  18: 0x1768fdcc 0x400037c     0x18                CLASS   instance 0x0a8ce05c hashCodeProvider
  19: 0x1768fdcc 0x400037d     0x1c                CLASS   instance 0x00000000 onCollectionChangedDelegate
  20: 0x1768fdcc 0x400037e     0x20                CLASS   instance 0x00000000 onCollectionChangingDelegate
  21: 0x1768fdcc 0x400037f     0x24                CLASS   instance 0x00000000 onColumnPropertyChangedDelegate
  22: 0x1768fdcc 0x4000380     0x2c       System.Boolean   instance 0 fInClear


Then we continue to dump the "list" field () to get to the actual array of DataColumn objects:

   1: 0:025> !do 0x0a8cdf30 
   2: Name: System.Collections.ArrayList
   3: MethodTable 0x0226fcb4
   4: EEClass 0x022e4fb8
   5: Size 24(0x18) bytes
   6: GC Generation: 2
   7: mdToken: 0x02000100  (c:\windows\\framework\v1.1.4322\mscorlib.dll)
   8: FieldDesc*: 0x0226f8e8
   9:         MT      Field     Offset                 Type       Attr      Value Name
  10: 0x0226fcb4 0x4000362      0x4                CLASS   instance 0x0a8cdf48 _items
  11: 0x0226fcb4 0x4000363      0xc         System.Int32   instance 1 _size
  12: 0x0226fcb4 0x4000364     0x10         System.Int32   instance 2 _version
  13: 0x0226fcb4 0x4000365      0x8                CLASS   instance 0x00000000 _syncRoot


Now, we use the dd command to dump the memory (displayed as longs) at the address the _items field conatins:

   1: 0:025> dd 0x0a8cdf48 
   2: 0a8cdf48  01e33068 00000010 01e32160 0a8ce10c
   3: 0a8cdf58  00000000 00000000 00000000 00000000
   4: 0a8cdf68  00000000 00000000 00000000 00000000
   5: 0a8cdf78  00000000 00000000 00000000 00000000
   6: 0a8cdf88  00000000 00000000 00000000 00000000
   7: 0a8cdf98  0226ce68 0a8cdfcc 00000000 00000000
   8: 0a8cdfa8  00000000 00000000 00000000 00000001
   9: 0a8cdfb8  00000000 00000007 3f3851ec 00000001


This memory dump shows us the actual stored objects in the array, but note that the first three longs are used internally by .net to maintain the array itself. So, our first (and in this case only) DataColumn in the DataTable is stored at address 0x0a8ce10c. If we then execute !do on this address we get the following dump:

   1: 0:025> !do 0a8ce10c
   2: Name: System.Data.DataColumn
   3: MethodTable 0x1768dba0
   4: EEClass 0x1769677c
   5: Size 128(0x80) bytes
   6: GC Generation: 2
   7: mdToken: 0x02000017  (c:\windows\assembly\gac\\1.0.5000.0__b77a5c561934e089\
   8: FieldDesc*: 0x1768d2e0
   9:         MT      Field     Offset                 Type       Attr      Value Name
  10: 0x17387bf4 0x4000583      0x4                CLASS   instance 0x00000000 site
  11: 0x17387bf4 0x4000584      0x8                CLASS   instance 0x00000000 events
  12: 0x17387bf4 0x4000582        0                CLASS     shared   static EventDisposed
  13:     >> Domain:Value 0x000c53e8:NotInit  0x00123418:0x02a5ef58 <<
  14: 0x1768dba0 0x4000357     0x78       System.Boolean   instance 1 allowNull
  15: 0x1768dba0 0x4000358     0x79       System.Boolean   instance 0 autoIncrement
  16: 0x1768dba0 0x4000359      0xc         System.Int64   instance 1 autoIncrementStep
  17: 0x1768dba0 0x400035a     0x14         System.Int64   instance 0 autoIncrementSeed
  18: 0x1768dba0 0x400035b     0x24                CLASS   instance 0x00000000 caption
  19: 0x1768dba0 0x400035c     0x28                CLASS   instance 0x0a8ce0f4 _columnName
  20: 0x1768dba0 0x400035d     0x2c                CLASS   instance 0x0e4633dc dataType
  21: 0x1768dba0 0x400035e     0x30                CLASS   instance 0x0a461230 defaultValue
  22: 0x1768dba0 0x400035f     0x34                CLASS   instance 0x00000000 expression
  23: 0x1768dba0 0x4000360     0x64         System.Int32   instance -1 maxLength
  24: 0x1768dba0 0x4000361     0x68         System.Int32   instance 0 ordinal
  25: 0x1768dba0 0x4000362     0x7a       System.Boolean   instance 0 readOnly
  26: 0x1768dba0 0x4000363     0x38                CLASS   instance 0x00000000 sortIndex
  27: 0x1768dba0 0x4000364     0x3c                CLASS   instance 0x0a8cdaa8 table
  28: 0x1768dba0 0x4000365     0x7b       System.Boolean   instance 0 unique
  29: 0x1768dba0 0x4000366     0x6c         System.Int32   instance 1 columnMapping
  30: 0x1768dba0 0x4000367     0x70         System.Int32   instance 5862184 hashCode
  31: 0x1768dba0 0x4000368     0x74         System.Int32   instance 0 errors
  32: 0x1768dba0 0x4000369     0x40                CLASS   instance 0x00000000 extendedProperties
  33: 0x1768dba0 0x400036a     0x44                CLASS   instance 0x00000000 onPropertyChangingDelegate
  34: 0x1768dba0 0x400036b     0x48                CLASS   instance 0x0a8ce214 storage
  35: 0x1768dba0 0x400036c     0x1c         System.Int64   instance 0 autoIncrementCurrent
  36: 0x1768dba0 0x400036d     0x4c                CLASS   instance 0x00000000 _columnUri
  37: 0x1768dba0 0x400036e     0x50                CLASS   instance 0x0e460224 _columnPrefix
  38: 0x1768dba0 0x400036f     0x54                CLASS   instance 0x00000000 encodedColumnName
  39: 0x1768dba0 0x4000370     0x58                CLASS   instance 0x0e460224 description
  40: 0x1768dba0 0x4000371     0x5c                CLASS   instance 0x0e460224 dttype
  41: 0x1768dba0 0x4000372     0x60                CLASS   instance 0x00000000 simpleType

Now, we can see the field "storage", and if we dump this object we se the following:

   1: 0:025> !do 0x0a8ce214 
   2: Name: System.Data.Common.StringStorage
   3: MethodTable 0x176b2d24
   4: EEClass 0x17698b70
   5: Size 24(0x18) bytes
   6: GC Generation: 2
   7: mdToken: 0x020000c6  (c:\windows\assembly\gac\\1.0.5000.0__b77a5c561934e089\
   8: FieldDesc*: 0x176b2bf4
   9:         MT      Field     Offset                 Type       Attr      Value Name
  10: 0x176b20cc 0x4000666      0x4                CLASS   instance 0x0e4633dc type
  11: 0x176b20cc 0x4000667      0x8                CLASS   instance 0x0a8cdaa8 table
  12: 0x176b20cc 0x4000668      0xc                CLASS   instance 0x00000000 dbNullBits
  13: 0x176b2d24 0x4000762     0x10                CLASS   instance 0x140306c0 values
  14: 0x176b2d24 0x4000761        0                CLASS     shared   static DBNullObject
  15:     >> Domain:Value 0x000c53e8:NotInit  0x00123418:0x02a6b7c0 <<

And, finally we can now use our "dumparray" script to dump all the values for this column like so:

   1: $$>a< dumparray 0x140306c0 

And finally, we now get our values, in this case a bunch of Guids stored as string:

   1: String: de8888e3-7e3e-4055-b5b7-6941153dbfd1
   3: String: de8a18a2-d8b2-48b8-9549-15deeedb48d2
   5: String: de95babf-4b7d-42ae-ab91-98d7d3e9daeb
   7: String: de98657b-4eac-430d-a78e-3943606fc81b
   9: String: de9bd9b3-a677-43c4-b1a9-211b06897d2e
  11: String: de9e4499-cafd-4630-94c9-1f27236cc660
  13: String: de9f3f56-f2db-4754-9ff9-c9b52aeaae70
  15: String: dea031c2-73bd-4a6c-abab-4064810ff93f
  17: String: dea1eb30-f19c-41a9-b632-87349709de56
  19: String: dea39f96-bd59-451a-86c7-2defa8077150
  21: String: deac24cc-5d3e-47f1-a767-b402c927815a
  23: String: deaed11e-3fa3-49f0-9341-a4e2be75c661
  25: String: deb11d08-cbdf-433e-910c-ba7dfa81f746
  27: String: deb6ef08-e8d7-43e8-a5c9-27d092842b74
  29: String: deb703e1-ae3a-461d-bfb8-b884e1c8ef53
  31: String: deb70ded-1fb8-4897-9ade-8923e1eba10f



19 March 2008


  1. Great post, thank you. I was able to follow this to extract row values for 2.0 DataTables as well - somewhat easier with the built-in !da command (!do -v was deprecated with 1.1). Still nowhere near as easy as it should be:
Post a comment    
User verification Image for user verification  
EPiTrace logger