Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

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:
    36,713
    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:
    36,713
    As my Daddy always says, "First you get the pointer, THEN you get the power."