Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues 1870 and 12790: Write out string mixin code to a file for debugging purpose #8677

Merged
merged 1 commit into from
Nov 7, 2018

Conversation

thewilsonator
Copy link
Contributor

There is no test case for this yet, I'm not sure how to check the contents of generated files with dmd's test suite. I also don't know how to test debug info, any help appreciated.

cc @TurkeyMan

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @thewilsonator! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the annotated coverage diff directly on GitHub with CodeCov's browser extension
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Auto-close Bugzilla Severity Description
1870 enhancement Reproduce offending lines in error messages for string mixins
12790 enhancement Compiler should keep mixin file around for debugging purposes

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub fetch digger
dub run digger -- build "master + dmd#8677"

@ghost
Copy link

ghost commented Sep 9, 2018

doesn't the source file mars.d need to be updated to activate the new option ?

@thewilsonator
Copy link
Contributor Author

True I forgot to sync before opening the PR

src/dmd/cli.d Outdated Show resolved Hide resolved
@RazvanN7
Copy link
Contributor

RazvanN7 commented Sep 9, 2018

Does it really need to be a compiler switch? Why not do this by default whenever mixins are used? @WalterBright is usually against adding new switches. How hard is it to simply print the offending line without any line number?

Also, if this gets merged it needs a changelog entry, updating the documentation and also the messages that dmd outputs if no parameter is provided.

@rainers
Copy link
Member

rainers commented Sep 9, 2018

There is also https://issues.dlang.org/show_bug.cgi?id=5051. My proposed patch writes mixins to a single file, otherwise your object directory might be filled with an ever increasing number of small files.

Visual D registers .mixin files with the D language service, so you get syntax highlighting and "auto" watches. I have not tested this since 2010, though.

I also don't know how to test debug info, any help appreciated.

For windows, there is testpdb.d with some examples, e.g. it should be possible to enumerate line number information for a function.

@jacob-carlborg
Copy link
Contributor

jacob-carlborg commented Sep 9, 2018

I'm not sure how to check the contents of generated files with dmd's test suite

A test can consist of a shell script, there are existing examples. Perhaps look at a test for Ddoc.

@@ -151,6 +151,7 @@ struct Param
// https://issues.dlang.org/show_bug.cgi?id=16997
bool vsafe; // use enhanced @safe checking
bool ehnogc; // use @nogc exception handling
bool debugMixins; // write out argument to mixin so that the debuuger has source
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ddoc comment please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the others do, also there is cli.d

src/dmd/parse.d Outdated
char* filename = cast(char*)mem.xmalloc(strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1);
sprintf(filename, "%s-mixin-%d", loc.filename, cast(int)loc.linnum);
scanloc.filename = filename;
auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this can be const.

src/dmd/parse.d Outdated

if (global.params.debugMixins)
{
auto tmp = FileName.combine(global.params.objdir,filename.ptr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this can be const.

src/dmd/parse.d Outdated
{
auto tmp = FileName.combine(global.params.objdir,filename.ptr);
filename = cast(char[])tmp[0 .. strlen(tmp)];
auto f = File.create(filename.ptr);
Copy link
Contributor

@dhasenan dhasenan Sep 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is certainly better than the status quo.

However, consider the following code:

void foo(string s)() { mixin(s); }
void main()
{
  foo!(`writeln(a);`);
  foo!(`writeln("hello world");`);
}

This will write writeln(a); to file scratch.d-mixin-5, then write writeln("hello world"); to the same file, overwriting what was there previously.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that likely to ever occur in real code?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any mixin that occurs inside a template that's instantiated more than once. That might be a minority of cases, but it certainly does occur. For instance, std.typecons has at least a handful of these. I wrote some code to automatically extract a postgresql row into a struct, and it contains mixins inside templates that are instantiated multiple times.

Also the standard shortcut for implementing opBinary is with mixins. That's why we switched to opBinary!operator, instead of opAdd and friends.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that likely to ever occur in real code?

I think it happens more often than not.
In my experience, most string mixins are wrapped in a mixin template for sanitary reasons, or in a template function. If the enclosing scope is not a template, then there's little motivation for a string mixin... you could just put the code directly. I reckon it's the common case for a mixin to appear in a parametric environment.

Copy link
Member

@WalterBright WalterBright left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When creating PRs for bugzilla issues, please include a link in the issue to the PR. This prevents multiple people from unwittingly working on the same issue. And even if it is rejected, it provides valuable information to anyone trying again.

I'm tired of repeatedly pointing this out to everyone making PRs.

@thewilsonator
Copy link
Contributor Author

thewilsonator commented Sep 9, 2018

@RazvanN7 This will result in a lot of unnecessary I/O for projects with lots of mixins, not to mention disk space. This is also so that the debugger has source so it needs to be on disk for that.

Also, if this gets merged it needs a changelog entry,

Will do. Done.

updating the documentation

isn't that what cli.d is for?

the messages that dmd outputs if no parameter is provided.

This is a parameterless switch.

src/dmd/parse.d Outdated

if (global.params.debugMixins)
{
auto tmp = FileName.combine(global.params.objdir,filename.ptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

      const tmp = FileName.combine(global.params.objdir.toDString(),filename);

Will give you a slice.

@Geod24
Copy link
Member

Geod24 commented Sep 10, 2018

When submitting a PR, please provide a title which allow us to see what it's about from the notification.

Also, is there no better way to provide this to GDB than writing it to a file ? @ibuclaw ?

@thewilsonator
Copy link
Contributor Author

As I understand it, the problem is that source locations are provided to the debugger (GDB/LLDB/whatever MS uses) but the source doesn't exist on disk, because its expanded at compile time, and so b somewhereinamixin will work, but show me the source where I am fails.

@Geod24
Copy link
Member

Geod24 commented Sep 10, 2018

I understand the problem, and agree that it needs fixing, I'm just wondering if debuggers have another way to cope with this than writing a file to disk.
And if we do end up writing a file to disk, I think it should live alongside objects file.

@thewilsonator
Copy link
Contributor Author

thewilsonator commented Sep 10, 2018

Can you embed source in an object file? I can't think of any other way to do it.

And if we do end up writing a file to disk, I think it should live alongside objects file.

Hence this is written toobjdir.

src/dmd/parse.d Outdated
sprintf(filename, "%s-mixin-%d", loc.filename, cast(int)loc.linnum);
scanloc.filename = filename;
auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1;
char[] filename = cast(char[])mem.xmalloc(len)[0 .. len];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cast to a pointer then slice. Casting arrays incur additional checks.

src/dmd/parse.d Outdated
filename = cast(char[])tmp[0 .. strlen(tmp)];
auto f = File.create(filename.ptr);
// We're going to write it out straight away so the cast below is fine
f.setbuffer(cast(void*)input.ptr,input.length);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: space after comma (other places in the diff as well)

src/dmd/parse.d Outdated
{
auto tmp = FileName.combine(global.params.objdir,filename.ptr);
filename = cast(char[])tmp[0 .. strlen(tmp)];
auto f = File.create(filename.ptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use new File(filename.ptr). create is a C++ wrapper.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually doesn't need to even be new'd

@thewilsonator
Copy link
Contributor Author

Addressed all comments except doc comment for global.params.debugMixins and collision between mixing in a template parameter. Added changeling and tests for 1879/12790. Haven't added a test for debug info generation

Copy link
Member

@wilzbach wilzbach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to avoid creating temporary files that aren't in test_results

Copy link
Member

@wilzbach wilzbach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to avoid creating temporary files that aren't in test_results

Copy link
Member

@wilzbach wilzbach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to avoid creating temporary files that aren't in test_results

@PetarKirov
Copy link
Member

PetarKirov commented Oct 23, 2018

@ZombineDev It would effectively be under whatever licence LLVM uses

@thewilsonator I didn't mean to port the source code to D, but to implement the features you miss in the existing test suite code.

@thewilsonator
Copy link
Contributor Author

Rebased. Fixed style.

No "mixin stack" yet. While nice, I'm not (yet) convinced that it will be necessary, and that can be done later if it is.

@thewilsonator
Copy link
Contributor Author

All green except semaphore which is segfaulting. @rainers any idea why?

@rainers
Copy link
Member

rainers commented Nov 1, 2018

All green except semaphore which is segfaulting. @rainers any idea why?

Not sure, an earlier build reports "double free or corruption".

Maybe it has to with flushMixin doing something unexpected when executed by atexit.

@rainers
Copy link
Member

rainers commented Nov 1, 2018

No "mixin stack" yet. While nice, I'm not (yet) convinced that it will be necessary, and that can be done later if it is.

Ok, being opt-in should not block it for dmd to progress.

I'd love to enable this feature in Visual D by default, but not being able to easily jump to the actual source of an error is probably a deal breaker.

OutBuffer* mixinOut; // write expanded mixins for debugging
const(char)* mixinFile; // .mixin file output name
int mixinLines; // Number of lines in writeMixins

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mixinOut and mixinLines should better go into the Global struct rather than Param. I guess you followed the moduleDeps example, but I'd consider it a bad one.

src/dmd/mars.d Outdated
@@ -1508,6 +1525,21 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param
params.map = true;
else if (arg == "-multiobj")
params.multiobj = true;
else if (startsWith(p + 1, "mixin="))
{
if (params.mixinFile)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This follows the -deps example, too, but no other option disallows overriding previous settings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix.

@@ -242,7 +279,7 @@ final class Parser(AST) : Lexer
//printf("Parser::Parser()\n");
scanloc = loc;

if (loc.filename)
if (!writeMixin(input, scanloc) && loc.filename)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not too happy about writeMixin being in the parse module. Maybe it would be better to do this (and the existing pseudo-filename creation) at the callsite with a small helper function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that originally, but that constructor is called from MixinStatement and MixinDeclaraion and I didn't really want to duplicate the code.

@thewilsonator
Copy link
Contributor Author

Not sure, an earlier build reports "double free or corruption".

Aha. I fix the old problem with main's argv parameter no longer being valid in atexit, so I guess params.mixinOut being new'd means the GC might be getting to it?

@thewilsonator thewilsonator force-pushed the mixin-debug branch 2 times, most recently from 4e34ddc to 5038008 Compare November 1, 2018 12:17
@thewilsonator
Copy link
Contributor Author

@rainers any idea why testing of --DRT-trapExceptions=0 has started failing?

#4  0x00000000004276f1 in D main ()
...
grep "in D main (args=...) at src/rt_trap_exceptions_drt.d:9" > /dev/null < ./generated/linux/release/64/rt_trap_exceptions_drt_gdb.output
make[2]: *** [generated/linux/release/64/rt_trap_exceptions_drt_gdb.done] Error 1

or why (args=...) has disappeared?

@thewilsonator
Copy link
Contributor Author

Unrelated adding 72h -> merge

@thewilsonator thewilsonator added 72h no objection -> merge The PR will be merged if there are no objections raised. auto-merge and removed 72h no objection -> merge The PR will be merged if there are no objections raised. labels Nov 4, 2018
@thewilsonator thewilsonator self-assigned this Nov 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.