Search Unity

Visual Studio, pointers and debugging

Discussion in 'Scripting' started by PsycHead, Jan 11, 2019.

  1. PsycHead

    PsycHead

    Joined:
    Sep 14, 2012
    Posts:
    17
    While working with unmanaged code in Unity and Visual Studio I noticed that VS gets confused about certain operations when debugging.

    For instance arithemtic on pointers to value types, like this:

    SomeStruct* somePointer;
    SomeStruct* anotherPointer = somePointer + 1;

    result in a pointer that is offset by 8 Byte. Similarly sizeof(SomeStruct) can't be evaluated by VS.
    Mind you, the code works fine, this is just a Visual Studio debugging issue.
    It works as expected in .net framework and .net core environments.

    Does anyone know a solution? It's virtually impossible to debug unsafe code in Unity like this.
    Tested with Unity 2018.3 (both Mono and IL2CPP backends) and Visual Studio 2017.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    3,244
    Is it consistently off? VS does various precompiles of the headers (*.pch files) and those can sometimes get out of sync but usually a build-all fixes it. Or delete them and rebuild all.

    Also, the above is obviously legitimate if your struct is 8 bytes in size. :)

    ALSO... if this isn't your code, some coders make typecasts for structure pointers rather than using the struct, like so:

    Code (csharp):
    1. #include <stdlib.h>
    2. #include <stdio.h>
    3.  
    4. typedef struct
    5. {
    6.     int a;
    7.     int b,c,d,e,f;
    8. } _farg;
    9.  
    10. typedef _farg* farg;        // struct to struct ptr alias
    11.  
    12. int main( int argc, char **argv)
    13. {
    14.     // these are deceptively actually pointers to _farg struct pointers!
    15.     farg* fp1 = 0;
    16.     farg* fp2 = fp1 + 1;
    17.  
    18.     printf( "fp1:%p\n", fp1);
    19.     printf( "fp2:%p\n", fp2);
    20.  
    21. // this line won't compile because it's a pointer alias!
    22. //    fp1->a = 0;
    23.  
    24. // this line will compile but obviously segfault because of my zero above
    25.     (*fp1)->a = 0;   // will compile but segfault
    26. }
    If that is what your "SomeStruct" identifier actually is, then obviously you are pointing to a pointer, so in 64bit architecture it is always gonna be 8 bytes from the previous one, plus sizeof will be 8.
     
  3. PsycHead

    PsycHead

    Joined:
    Sep 14, 2012
    Posts:
    17
    Rebuilding and deleting temporary files does not help.

    The following MonoBehavior can be used to reproduce the issue:

    Code (CSharp):
    1. using System;
    2. using System.Runtime.InteropServices;
    3. using UnityEngine;
    4.  
    5. public class BugRepro : MonoBehaviour
    6. {
    7.     void Awake()
    8.     {
    9.         Bug();
    10.     }
    11.  
    12.     public static void Bug()
    13.     {
    14.         unsafe
    15.         {
    16.             int someStructSize = sizeof(A);
    17.             var structHandle = (A*)Marshal.AllocHGlobal(someStructSize * 2);
    18.  
    19.             if (structHandle != null)
    20.             {
    21.                 structHandle[0] = new A(0, 1, 2);
    22.                 structHandle[1] = new A(42, 43, 44);
    23.  
    24.                 // when debugging at this point, the expression *(structHandle + 1) will evaluate falsely in VS
    25.                 // sizeof(A) won't work either
    26.                 Marshal.FreeHGlobal((IntPtr)structHandle);
    27.             }
    28.         }
    29.     }
    30.  
    31.     public struct A
    32.     {
    33.         public int SomeInt;
    34.         public short SomeShort;
    35.         public PartOfA SomeStruct;
    36.  
    37.         public A(int a, short b, uint c)
    38.         {
    39.             SomeInt = a;
    40.             SomeShort = b;
    41.             SomeStruct = new PartOfA(c);
    42.         }
    43.  
    44.         public override string ToString()
    45.         {
    46.             return $"{SomeInt} {SomeShort} {SomeStruct.SomeUint}";
    47.         }
    48.     }
    49.  
    50.     public struct PartOfA
    51.     {
    52.         public uint SomeUint;
    53.  
    54.         public PartOfA(uint value)
    55.         {
    56.             SomeUint = value;
    57.         }
    58.     }
    59. }
     
  4. PsycHead

    PsycHead

    Joined:
    Sep 14, 2012
    Posts:
    17
    Rider evaluates the pointers correctly, this is a Visual Studio issue. Can someone try to reproduce it on an older (VS 2015) version please?
     
  5. PsycHead

    PsycHead

    Joined:
    Sep 14, 2012
    Posts:
    17
    Got answer by their support:

    Cheers.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    3,244
    As my Daddy always says, "First you get the pointer, THEN you get the power."