www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How do you debug safe nogc code? Can't figure out how to print.

reply aliak <something something.com> writes:
Hi, I previously had trouble trying to get print statements 
working while debugging in nogc. And that was hackishly solved 
[0], but I can't figure out how to do the same if I have  safe 
involved. Here's a cut down sample:

// This is the function from the thread referenced
auto assumeNoGC(T)(T t) {
     import std.traits;
     enum attrs = functionAttributes!T | FunctionAttribute.nogc;
     return cast(SetFunctionAttributes!(T, functionLinkage!T, 
attrs)) t;
}

private struct Dispatcher {
     public template opDispatch(string name){
         auto ref opDispatch(Args...)(auto ref Args args) {
             import std.stdio;
             // Print args here for debugging purposes
             assumeNoGC({ writeln(args); })();
             return 3;
         }
     }
}

 safe  nogc void main() {
     auto i = Dispatcher().getI(3);
}

So, basically with the above, using:
     debug writeln(args);
Causes "Error:  nogc function D main cannot call non- nogc 
function"

Using:
     debug assumeNoGC({ writeln(args); })();
Causes:
     Error:  safe function D main cannot call  system function
     Error:  nogc function D main cannot call non- nogc function

Changing attrs in assumeNoGC
     enum attrs = functionAttributes!T | FunctionAttribute.nogc | 
FunctionAttribute.safe
Causes same as previous errors

Adding  trusted to declaration of opDispatch gets rid of  safe 
error but I still get "Error:  nogc function D main cannot call 
non- nogc function". And going through the codebase and figuring 
out where to add  trusted is *very* cumbersome.

I can't figure out how to make this work. The ideal way would be 
just a debug_print() function that works in nogc and safe code. 
I.e. does not affect the attribute inference of templates.

Cheers,
- Ali

[0] https://forum.dlang.org/post/pmf4dm$jmo$1 digitalmars.com
Nov 16 2018
next sibling parent reply Zoadian <no no.no> writes:
debug {
     import std.stdio;
     writeln(args);
}
Nov 16 2018
parent aliak <something something.com> writes:
On Friday, 16 November 2018 at 13:03:40 UTC, Zoadian wrote:
 debug {
     import std.stdio;
     writeln(args);
 }
As mentioned in the original post, that does not work.
Nov 17 2018
prev sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Friday, 16 November 2018 at 12:59:22 UTC, aliak wrote:

 Adding  trusted to declaration of opDispatch gets rid of  safe 
 error but I still get "Error:  nogc function D main cannot call 
 non- nogc function". And going through the codebase and 
 figuring out where to add  trusted is *very* cumbersome.

 I can't figure out how to make this work. The ideal way would 
 be just a debug_print() function that works in nogc and safe 
 code. I.e. does not affect the attribute inference of templates.
As Zoadian points out, you can now (since 2.079: https://dlang.org/changelog/2.079.0.html#bugfix-list) shamelessly call nogc inside debug blocks. To get at where the problematic areas were in the code in question though, it's as follows:
 auto assumeNoGC(T)(T t) {  // point 1
     import std.traits;
     enum attrs = functionAttributes!T | FunctionAttribute.nogc;
     // point 2:
     return cast(SetFunctionAttributes!(T, functionLinkage!T, 
 attrs)) t;
 }
At 'point 1', you just take by value. If `t` ends up being a closure, D will allocate. That's where it breaks the nogc. Solution: auto assumeNoGC(T)(return scope T t) { /* ... */ } Now you take (and return) a `scope` t, in that case when `t` is a closure D won't allocate it on the GC heap. At 'point 2' you make an un- safe cast, that's where it breaks safe. Given that the whole deal is just a debugging hack, you could mark the whole function trusted: auto assumeNoGC(T)(return scope T t) trusted { /* ... */ }
Nov 16 2018
parent reply aliak <something something.com> writes:
On Friday, 16 November 2018 at 13:21:39 UTC, Stanislav Blinov 
wrote:
 At 'point 1', you just take by value. If `t` ends up being a 
 closure, D will allocate. That's where it breaks the  nogc. 
 Solution:

 auto assumeNoGC(T)(return scope T t) { /* ... */ }

 Now you take (and return) a `scope` t, in that case when `t` is 
 a closure D won't allocate it on the GC heap.

 At 'point 2' you make an un- safe cast, that's where it breaks 
  safe. Given that the whole deal is just a debugging hack, you 
 could mark the whole function  trusted:

 auto assumeNoGC(T)(return scope T t)  trusted { /* ... */ }
Sawweet! Thanks, that made the printing possible! "scope" is const from what I understand right? It works without scope as well. So just "return T". Cheers, - Ali
Nov 17 2018
next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 17 November 2018 at 13:13:36 UTC, aliak wrote:

 Sawweet! Thanks, that made the printing possible!
You're welcome ;) Still, try a more recent compiler. This works fine: void foo() nogc { debug { import std.stdio; writefln("%d", 42); } }
 "scope" is const from what I understand right? It works without 
 scope as well. So just "return T".
No, "scope" means "does not escape scope", i.e. you can't assign that argument to some global. The only exception is through a return, in which case "return" also needed. Whether or not just "return" is sufficient, is a bit out there still (AFAIK), between DIP25, DIP1000 and current state of the language. "scope" was implemented for delegates for ages now, exactly to allow passing lambdas around without allocating their context on the GC heap.
Nov 17 2018
parent reply aliak <something something.com> writes:
On Saturday, 17 November 2018 at 13:43:20 UTC, Stanislav Blinov 
wrote:
 On Saturday, 17 November 2018 at 13:13:36 UTC, aliak wrote:

 Sawweet! Thanks, that made the printing possible!
You're welcome ;) Still, try a more recent compiler. This works fine: void foo() nogc { debug { import std.stdio; writefln("%d", 42); } }
Yeah that does, but not the code I posted -> https://run.dlang.io/is/vH3cFa You can use "debug blah" to hide inside functions that are attributed, but when you have an attributed function that calls a template, attribtues of which are supposed to be inferred, it seems to fail. Maybe a bug if it's supposed to work?
 "scope" is const from what I understand right? It works 
 without scope as well. So just "return T".
No, "scope" means "does not escape scope", i.e. you can't assign that argument to some global. The only exception is through a return, in which case "return" also needed. Whether or not just "return" is sufficient, is a bit out there still (AFAIK), between DIP25, DIP1000 and current state of the language. "scope" was implemented for delegates for ages now, exactly to allow passing lambdas around without allocating their context on the GC heap.
Aha, cool. Thanks!
Nov 17 2018
next sibling parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 17 November 2018 at 13:55:24 UTC, aliak wrote:

 You can use "debug blah" to hide inside functions that are 
 attributed, but when you have an attributed function that calls 
 a template, attribtues of which are supposed to be inferred, it 
 seems to fail.

 Maybe a bug if it's supposed to work?
Indeed that fails, and I would say that's a bug. If it's allowed in nogc, it shouldn't then infer to non- nogc when not explicitly attributed. Maybe debug blocks should outright be skipped when inferring?..
Nov 17 2018
prev sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Sat, 17 Nov 2018 13:55:24 +0000, aliak wrote:
 You can use "debug blah" to hide inside functions that are attributed,
 but when you have an attributed function that calls a template,
 attribtues of which are supposed to be inferred, it seems to fail.
You can explicitly mark a templated function as nogc. If you want your function's nogc-ness inferred, you can pull out the debug logging into a separate function and explicitly mark it nogc.
Nov 17 2018
parent reply aliak <something something.com> writes:
On Saturday, 17 November 2018 at 17:48:43 UTC, Neia Neutuladh 
wrote:
 On Sat, 17 Nov 2018 13:55:24 +0000, aliak wrote:
 You can use "debug blah" to hide inside functions that are 
 attributed, but when you have an attributed function that 
 calls a template, attribtues of which are supposed to be 
 inferred, it seems to fail.
You can explicitly mark a templated function as nogc. If you want your function's nogc-ness inferred, you can pull out the debug logging into a separate function and explicitly mark it nogc.
Could do. But it's not scalable. I'd have to comment out all the unittests that call the template function with a T that allocates inside the nogc template (if I understood you correctly that it)
Nov 17 2018
parent reply Neia Neutuladh <neia ikeran.org> writes:
On Sat, 17 Nov 2018 21:16:13 +0000, aliak wrote:
 Could do. But it's not scalable. I'd have to comment out all the
 unittests that call the template function with a T that allocates inside
 the  nogc template (if I understood you correctly that it)
I meant something like: void debugln(T...)(T args) nogc { import std.stdio; debug(MyProject) writeln(args); } You use that function instead of writeln in your nogc-compatible templates: void callFunc(alias func)() { debugln("about to call function!"); func(); debugln("done calling function!"); } Then I can write: nogc: void foo() { printf("hello world\n"); } void main() { callFunc!foo(); }
Nov 17 2018
parent reply aliak <something something.com> writes:
On Saturday, 17 November 2018 at 21:56:23 UTC, Neia Neutuladh 
wrote:
 On Sat, 17 Nov 2018 21:16:13 +0000, aliak wrote:
 Could do. But it's not scalable. I'd have to comment out all 
 the unittests that call the template function with a T that 
 allocates inside the  nogc template (if I understood you 
 correctly that it)
I meant something like: void debugln(T...)(T args) nogc { import std.stdio; debug(MyProject) writeln(args); } You use that function instead of writeln in your nogc-compatible templates: void callFunc(alias func)() { debugln("about to call function!"); func(); debugln("done calling function!"); } Then I can write: nogc: void foo() { printf("hello world\n"); } void main() { callFunc!foo(); }
Aha! I misunderstood what you meant. Yes that's actually simpler that what I was doing :D Thanks!
Nov 18 2018
parent Vijay Nayar <madric gmail.com> writes:
On Sunday, 18 November 2018 at 11:00:26 UTC, aliak wrote:
 On Saturday, 17 November 2018 at 21:56:23 UTC, Neia Neutuladh 
 wrote:
 On Sat, 17 Nov 2018 21:16:13 +0000, aliak wrote:
 [...]
I meant something like: void debugln(T...)(T args) nogc { import std.stdio; debug(MyProject) writeln(args); } You use that function instead of writeln in your nogc-compatible templates: void callFunc(alias func)() { debugln("about to call function!"); func(); debugln("done calling function!"); } Then I can write: nogc: void foo() { printf("hello world\n"); } void main() { callFunc!foo(); }
Aha! I misunderstood what you meant. Yes that's actually simpler that what I was doing :D Thanks!
Alternatively, you can use the core.stc libraries, which do not depend on the GC, for debugging. https://dlang.org/phobos/core_stdc_stdio.html#.printf
Nov 22 2018
prev sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Saturday, 17 November 2018 at 13:13:36 UTC, aliak wrote:
 On Friday, 16 November 2018 at 13:21:39 UTC, Stanislav Blinov 
 wrote:
 auto assumeNoGC(T)(return scope T t)  trusted { /* ... */ }
Sawweet! Thanks, that made the printing possible! "scope" is const from what I understand right? It works without scope as well. So just "return T".
No, `in` used to mean const scope. scope means roughly "this thing does not escape this function" e.g. assigning to global variables.
Nov 17 2018
parent aliak <something something.com> writes:
On Saturday, 17 November 2018 at 13:46:00 UTC, Nicholas Wilson 
wrote:
 On Saturday, 17 November 2018 at 13:13:36 UTC, aliak wrote:
 On Friday, 16 November 2018 at 13:21:39 UTC, Stanislav Blinov 
 wrote:
 auto assumeNoGC(T)(return scope T t)  trusted { /* ... */ }
Sawweet! Thanks, that made the printing possible! "scope" is const from what I understand right? It works without scope as well. So just "return T".
No, `in` used to mean const scope. scope means roughly "this thing does not escape this function" e.g. assigning to global variables.
Righto! Thanks!
Nov 17 2018