BUG: debugger losing variable values

over the weekend I had occasion to write a line of code that added two random byte values.  I made the mistake of not contemplating the case where the resulting sum produced a result that was too large for the underlying data type to hold -- this turned out to be a nightmare to debug because VS 2005 beta 2 would lose the values of the variables involved when the exception was thrown.

here is a sketch of the code that caused that issue:

at the class level these variables are defined:

Private Const STATE_LENGTH_M1 As Integer = 256 - 1
Private _state(STATE_LENGTH_M1) As Byte

within an instance method, something like the following was the cause of the problem (simplified -- the debugger still loses the values in this simple case, btw):

Dim n As Byte = 0
'....
n = _state(i) + _state(j)

Although the fix to this code was pretty trivial, when the IDE broke on this line, the values contained in array _state were "undefined" which made it tough to identify the cause (since the line was more complicated)

Here was the fix:

Dim n As Integer = 0
n = (CType(_state(i), Integer) + CType(_state(j), Integer)) Mod 256
'use the value of n in a subsequent expression as Convert.ToByte(n)

Since the IDE had "lost" the values, this simple bug took much longer to fix than it should have...






Answer this question

BUG: debugger losing variable values

  • suparba

    Question 2) The message is generated at run time to replace the typename with the actual type (in this case Byte):
    I searched the integrated help for "Constant expression not representable in type" and the first topic was the one you probably are looking for:
    Constant expression not representable in type '<typename>'

    Question 1) You are right, F1 on the watch window bring up general help about the watch window, that I think is the expected behavior (help on individual exceptions is available using the exception assistant)

    Question 3) Yes, it is true, sometimes the error message from the watch window is different than the one in code, but in this scenario I think it is a pretty good error message (especially since I can see the value of _state(i) and _state(j))

    Still, too bad I could not reproduce the scenario you run into where the values were not displayed.


  • Yury_A

    that message might have been helpful (although a bit vague) -- assuming that the values of _state(i) and _state(j) were "watchable".  since the values of _state(i) and _state(j) were missing, the message would have been meaningless.  further testing has revealed that this debug bug is non-deterministic -- it didn't occur in the test I ran immediately prior to posting this reply.  This shows that the IDE behaves differently at times which is less than desirable since restarting the IDE would be the last thing to come to mind when trying to debug a problem in code that you have just written...

    the specific watch window message does beg three questions, however.  1) "Why aren't these messages searchable in the integrated help from the watch window " 2) "Why aren't these messages in the integrated help " and 3) "Why doesn't the watch window throw the same error that the code does ".  First, Selecting the message in the watch window and pressing F1 results in general help about the watch window.  Next, Pasting the message into the search page yields no topically applicable results.  Finally, that the watch window and the code line yield different results suggests that the code and the watch window are not 100% in sync.

  • tbrierly

    Hi, stand__sure,

    I’m sorry to hear that this had you hung up for so long! I just tried this out on the final version of VS2005 – what I get is the exception dialog stating that there was an overflow exception, and when I examine the watch window at that point (or hover over the variables) I’m seeing the correct values in the byte array, so it looks like this is something that got addressed between releasing beta2 and the final product. If & when you upgrade to the final version, I’d really like to hear if this problem has indeed gone away for you – please let us know!



    --Matt--*

  • VirginiaA

    Thanks for the response.

    My December partner bundle just arrived and I plan to migrate over the weekend.  I will test this scenario out and see if it is still an issue.



  • Matt Waldron

    Thanks for reporting this issue but I am not sure how to repro it: I run the following code and the debugger will stop at the exception but the values in the array are not "undefined".

    Module
    Module1
       Sub Main()
          Dim i As New c
          i.OverFlow()
       End Sub
    Class c
       Private Const STATE_LENGTH_M1 As Integer = 256 - 1
       Private _state(STATE_LENGTH_M1) As Byte
       Sub OverFlow()
          Dim n As Byte = 0
          _state(1) = 200
          _state(2) = 200
          n = _state(1) + _state(2)
       End Sub
    End Class
    End
    Module

    If you have time to post the simplified code that caused the problem I could look into it.

    Luca
    VB Test Team


  • RichardGreenwell

    it was a short piece based upon the "CipherSaber" algorithm -- since the code for the whole class is short, I'll post it up -- the problem  has been fixed in the class listing below



    Dim n As Integer = 0
    '...
    n = (CType(_state(i), Integer) + CType(_state(j), Integer)) Mod 256

     


    which would have been


    Dim n As Byte = 0
    n = _state(i) + _state(j)

     


    as a note, I was also unable to duplicate the problem on my machine at work (Server 2003, VS Beta 2) -- my home machine is XP Pro SP2, VS Beta 2 -- doubt the OS makes a difference, but it might...

    class listing

    Imports System.Security
    Imports System.Security.Cryptography

    Public Interface ICipherSabre

     Property Key() As String

     ''' <summary>
     ''' Encrypts a String
     ''' </summary>
     Function Encrypt(ByVal PlainText As String) As Byte()

     ''' <summary>
     ''' Decrypts a String
     ''' </summary>
     Function Decrypt(ByVal CipherBytes() As Byte) As String

    End Interface

    Public Class CipherSabre
     Implements ICipherSabre

     Private Const IV_LENGTH_M1 As Integer = 10 - 1
     Private Const KEY_LENGTH_MAXIMUM_M1 As Integer = 256 - 1
     Private Const STATE_LENGTH_M1 As Integer = 256 - 1

     Private _iv(IV_LENGTH_M1) As Byte
     Private _key(KEY_LENGTH_MAXIMUM_M1) As Byte
     Private _state(STATE_LENGTH_M1) As Byte

     Public Sub New(ByVal Key As String)

      'initialize IV
      Dim gen As New RNGCryptoServiceProvider()
      gen.GetBytes(_iv)
      Me.Key = Key

      __SetStateArray()

     End Sub

     Public Sub New(ByVal Key As String, ByVal IV() As Byte)
      If (IV.Length = IV_LENGTH_M1 + 1) Then
       IV.CopyTo(_iv, 0)
      Else
       Exit Sub
      End If
      Me.Key = Key
      __SetStateArray()

     End Sub


     Public Function Decrypt(ByVal CipherBytes() As Byte) As String Implements ICipherSabre.Decrypt
      For nloop As Integer = 0 To IV_LENGTH_M1
       _iv(nloop) = CipherBytes(nloop)
      Next

      'initialize loop variables
      Dim i As Integer = 0
      Dim j As Integer = 0
      Dim n As Integer = 0

      'length of output string
      Dim nlength As Integer = CipherBytes.Length - IV_LENGTH_M1 - 1

      'input/output characters
      Dim CharOut(nlength) As Char

      'swap byte
      Dim byt As Byte

      'loop the byte array
      For nloop As Integer = 0 To nlength - 1
       Debug.WriteLine("iteration " & nloop.ToString())
       i = (i + 1) Mod (STATE_LENGTH_M1 + 1)
       j = (j + _state(i)) Mod (STATE_LENGTH_M1 + 1)
       byt = _state(i)
       _state(i) = _state(j)
       _state(j) = byt
       n = (CType(_state(i), Integer) + CType(_state(j), Integer)) Mod 256
       CharOut(nloop) = Convert.ToChar(CipherBytes(IV_LENGTH_M1 + 1 + nloop) Xor Convert.ToByte(n))
      Next

      Return (CharOut)

     End Function

     Public Function Encrypt(ByVal PlainText As String) As Byte() Implements ICipherSabre.Encrypt

      'initialize loop variables
      Dim i As Integer = 0
      Dim j As Integer = 0
      Dim n As Integer = 0

      'length of input string
      Dim nlength As Integer = PlainText.Length

      'input/output characters
      Dim bytOut(IV_LENGTH_M1 + nlength) As Byte
      _iv.CopyTo(bytOut, 0)
      'Dim characters() As Char = PlainText.ToCharArray

      'swap byte
      Dim byt As Byte

      'loop the string
      For nloop As Integer = 0 To nlength - 1
       Debug.WriteLine("iteration " & nloop.ToString())
       i = (i + 1) Mod (STATE_LENGTH_M1 + 1)
       j = (j + _state(i)) Mod (STATE_LENGTH_M1 + 1)
       byt = _state(i)
       _state(i) = _state(j)
       _state(j) = byt
       n = (CType(_state(i), Integer) + CType(_state(j), Integer)) Mod 256
       bytOut(IV_LENGTH_M1 + 1 + nloop) = Convert.ToByte(PlainText(nloop)) Xor Convert.ToByte(n)
      Next

      Return (bytOut)

     End Function

     Protected Overrides Sub Finalize()

     End Sub

     Public Property Key() As String Implements ICipherSabre.Key
      Get
       Return (BitConverter.ToString(_key))
      End Get
      Set(ByVal value As String)
       Dim keylen As Integer = value.Length + IV_LENGTH_M1 + 1
       If keylen > 256 Then
        Throw New ArgumentOutOfRangeException("Key", "Length of key must be less than 256 ASCII characters")
        Exit Property
       End If
       ReDim _key(keylen - 1)
       _iv.CopyTo(_key, 0)

       For nloop As Integer = 0 To (value.Length - 1)
        _key(IV_LENGTH_M1 + 1 + nloop) = Convert.ToByte(value(nloop))
       Next
      End Set
     End Property

     Private Sub __SetStateArray()
      'populate state array
      For nloop As Integer = 0 To STATE_LENGTH_M1
       _state(nloop) = Convert.ToByte(nloop)
      Next

      'index variables
      Dim j As Integer = 0 'following the ciphersaber convention
      Dim n As Integer 'following the ciphersaber convention

      'repeatedly calculating this in the loop makes no sense
      Dim keylength As Integer = _key.Length

      'swap variable
      Dim byt As Byte

      'mix the state array
      For i As Integer = 0 To STATE_LENGTH_M1
       n = i Mod keylength
       j = (j + _state(i) + _key(n)) Mod (STATE_LENGTH_M1 + 1)
       byt = _state(i)
       _state(i) = _state(j)
       _state(j) = byt
      Next

     End Sub
    End Class


     



  • GreyW63

    Thanks for posting the code.
    I tried on a recent build but I could not repro your problem: the values were displayed as expected and adding   _state(i) + _state(j) to the watch window gave me the very useful error:  Constant expression not representable in type 'Byte'. 

    Luca

  • BUG: debugger losing variable values