So I came across something strange that made me loose some time. I have been trying to print the content of an ArrayList containing string elements, sometimes, an element might contain an empty string, which is fine and absolutely my intention.
So I have something like this:
List<String> l = new ArrayList<String>();
//adding strings in l, sometimes it's an empty string
for (int i=0; i < l.size(); i++) {
Log.w("element in l : ", l.get(i));
}
So here, when the loop is gonna hit the empty string, logcat is simply NOT going to print it out BUT (and here is the root of my confusion), if you have a message following the one that failed to display, suddenly the failed message is going to show up as if it contained the new logcat message. For example if you try logging an empty string like this
Log.w(TAG, <empty string here>);
Logcat is going to output nothing at first, then, when it has a NEW message to display this is what it prints out (in this case the new message is some warning about AudioTrack):
08-21 17:06:02.265 13047-13047/company.myapp W/TAG﹕ [ 08-21 17:06:05.411 766: 937 W/AudioTrack ]
AUDIO_OUTPUT_FLAG_FAST denied by client
I'm interested in knowing how this happens, maybe it can help someone else not getting super confused like I did. I suppose trying to log an empty string triggers some kind of buffer that sits there until it gets something to print, is this a bug?
That is an interesting question. I just tried this in LogRabbit and am able to see the same result.
I took a quick browse through the android source and see that Log.W(...) ends up in native code and getting handled in logd_write.c
This basically writes the data to /dev/log/main (or one of the other logs)
You can get those logs like this:
adb pull /dev/log/events .
adb pull /dev/log/main .
adb pull /dev/log/radio .
adb pull /dev/log/system .
You will need to press cntl-C otherwise the copy will happen forever.
Looking in the raw log in /dev/log/main I see the message does get logged:
<8b>F×U^_<8c>^Y^U^Emfl_MessageList^#Before Empty^#^R^#^#^#!z^#^#!z^#^#
<8b>F×U^_<8c>^Y^U^Emfl_MessageList^#^#^]^#^#^#!z^#^#!z^#^#
<8b>F×U^_ <8c>^Y^U^Emfl_MessageList^#After Empty^#7^#^#^#^#^E^#^#^Z^E^#^#
That gets decoded by struct found in logger.h So I think this is a problem in adb. pull the source code from here: (looks like quite a few of undocumented commands there)
This is the primary function
static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
char* log_tags = getenv("ANDROID_LOG_TAGS");
std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
std::string cmd = "shell:export ANDROID_LOG_TAGS=\"" + quoted + "\"; exec logcat";
if (!strcmp(argv[0], "longcat")) {
cmd += " -v long";
}
--argc;
++argv;
while (argc-- > 0) {
cmd += " " + escape_arg(*argv++);
}
return send_shell_command(transport, serial, cmd);
}
Looking in there I see that all logcat does is basically this:
adb shell
> exec logcat
So I think the root of the problem is in logcat itself. Logcat.cpp calls into log_read.c
Based on my quick read through things what I think is happening is the message is not terminated properly. The empty message does not show up until another message is appended and the first message overruns and shows the second message because it has the appropriate termination.
Related
I want to pull a file from my android device through an adb command from my macOS application.
Everything works perfect with the code below, except when the name of the file I want to pull contains special characters like german umlauts (äöüÄÖÜ).
I get this error:
adb: error: failed to stat remote object '/storage/emulated/0/Download/Böse': No such file or directory.
But when I use the command adb pull /storage/emulated/0/Download/Böse ~/Desktop from within the Terminal.app, the file will be pulled to my computer.
The strange thing here is that if I copy the substring /storage/emulated/0/Download/Böse from the Xcode console output, the command is also not working within the Terminal.app until I delete the ö and replace it with an ö from my keyboard input.
I tried replacing the ö with the unicode representation \u{00f6}, but this has no effect (but the console output still shows an ö but the 'wrong' encoded one.
// Configure task.
let task = Process()
task.launchPath = "~/Library/Android/sdk/platform-tools/adb"
task.arguments = ["pull", "/storage/emulated/0/Download/Böse", "~/Desktop"]
// Configure pipe.
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
// Run task.
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)
task.waitUntilExit()
// adb: error: failed to stat remote object '/storage/emulated/0/Download/Böse': No such file or directory
print(output)
I found the following in the documentation, how the Process handles the arguments that I provide:
The NSTask object converts both path and the strings in arguments to appropriate C-style strings (using fileSystemRepresentation) before passing them to the task via argv[] . The strings in arguments do not undergo shell expansion, so you do not need to do special quoting, and shell variables, such as $PWD, are not resolved.
It seems like I am not the only one with this problem, and I found this workaround:
How to work around NSTask calling -[NSString fileSystemRepresentation] for arguments, but I was not able to make it work with Swift.
As a workaround I am now writing my adb command to a file and execute it from a bash command in my application.
let source = "/storage/emulated/0/Download/Böse"
let destination = "~/Desktop"
guard let uniqueURL = URL(string: destination + "/" + ProcessInfo.processInfo.globallyUniqueString) else { return }
// Write command to file
let scriptContent = "#!/bin/bash\n~/Library/Android/sdk/platform-tools/adb pull -a \"" + source + "\" \"" + destination + "\""
try? scriptContent.write(to: uniqueURL, atomically: false, encoding: .utf8)
// Configure task.
let task = Process()
task.environment = ["LC_ALL": "de_DE.UTF-8", "LANG": "de_DE.UTF-8"]
task.launchPath = "/bin/bash"
task.arguments = [uniqueURL.path]
// Configure pipe.
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
try? task.run()
// Run task.
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)
task.waitUntilExit()
print(output)
Even though this is working for now, it is not a satisfactory solution as it is not very elegant and not efficient too, so any improvements or better answers are welcome.
I want to create an AVD (Android Virtual Device) through command line in python. For that, I need to pass a string n to the stdin. I have tried the following
emulator_create = str(subprocess.check_output([android,'create', 'avd', '-n', emulator_name, '-t', target_id, '-b', abi],stdin=PIPE))
emulator_create.communicate("n")
but it raises the following error
raise CalledProcessError(retcode, cmd, output=output)
subprocess.CalledProcessError: Command '['/home/fahim/Android/Sdk/tools/android', 'create', 'avd', '-n', 'samsung_1', '-t', '5', '-b', 'android-tv/x86']' returned non-zero exit status 1
Process finished with exit code 1
What can I do?
There's something not working with your example. subprocess.check_output() returns the output from the child process you want to execute, not a handle to this process. In other words you get a string object (or maybe a bytes object) which you cannot use to manipulate the child process.
Probably what happens is that your script, using subprocess.check_output(), will execute the child process and wait until it is finished before continuing. But since you are never able to communicate with it, it will finish with a non-zero return value which will raise the subprocess.CalledProcessError
Now, using grep as an example of a command that waits on the standard input to execute something (since I don't have an Android Virtual Device creator installed) you could do this:
#!/usr/bin/env python2.7
import subprocess
external_command = ['/bin/grep', 'StackOverflow']
input_to_send = '''Almost every body uses Facebook
You can also say that about Google
But you can find an answer on StackOverflow
Even if you're an old programmer
'''
child_process = subprocess.Popen(args=external_command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
universal_newlines=True)
stdout_from_child, stderr_from_child = child_process.communicate(input_to_send)
print "Output from child process:", stdout_from_child
child_process.wait()
It will print "Output from child process: But you can find an answer on StackOverflow", which is the output from grep.
In this example, I have
Used the class subprocess.Popen to create an handle to the child process
Setting arguments stdin and stdout with the value subprocess.PIPE to enables us to communicate later on with this process.
Used its .communicate() method to send a string to its standard input. In the same step, I retrieved its standard output and standard error output.
Printed the standard output retrieved in the last step (just so to show that it is actually working)
Waited that this child process is finished
In Python 3.5, it's even simpler:
#!/usr/bin/env python3.5
import subprocess
external_command = ['/bin/grep', 'StackOverflow']
input_to_send = '''Almost every body uses Facebook
You can also say that about Google
But you can find an answer on StackOverflow
Even if you're an old programmer
'''
completed_process_result = subprocess.run(args=external_command,
input=input_to_send,
stdout=subprocess.PIPE,
universal_newlines=True)
print("Output from child process:", completed_process_result.stdout)
In this example, I have:
Used the module function subprocess.run() to execute the command.
The input argument is the string we send to the standard input of the child process
The return value is used later on to retreive the output of the child process
Now you have to adapt this code to your situation.
I have code that prints out the output of adb logcat into a wx.TextArea box, this all works great, click the button and logcat prints out, it prints out more when phone taps are made and everything works fine.
toolsDir and pkgName are both strings.
params = [toolsDir + "\\adb.exe", "logcat"]
p = Popen(params, stdout=subprocess.PIPE, bufsize=1)
for line in p.stdout:
self.progressBox.AppendText(line.decode('utf-8'))
However I have adapted this code to only print out the logs that are for a particular app, this is done using the windows 'findstr' function, adb logcat | findstr myApp. The code below initially works, but then stops, and doesn't display anything else whatever happens, button tapped, app closed etc.
Its like the buffer has reached the end and doesn't process any further events.
args = [toolsDir + '\\adb.exe', 'logcat']
args2 = ['findstr', pkgName]
process_adb = subprocess.Popen(args, stdout=subprocess.PIPE, shell=False)
process_fs = subprocess.Popen(args2, stdin=process_adb.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
for line in process_fs.stdout:
self.progressBox.AppendText(line.decode('utf-8'))
How come the bottom code, the one that filters the app name, stops printing out real time logs, but the top doesn't? I'm guessing its something to do with the piping of one command to another.
You can use Python to filter lines:
if pkgName in line:
BTW: it can be problem with two processes because probably second waits for EOF (end of file) or other signal (ie. closed pipe) but its stdin is open all the time waiting for new data from first process..
Check this: python-subprocess-interaction-why-does-my-process-work-with-popen-communicate
I am trying to write an interface for the adb logcat command with node.js, and need some help having my function do continuous and asynchronous callbacks. This command generates constant output that I want to send to the callback function as it is generated (not after I let it run for awhile and then stop it). Is this possible with node.js, and if so how do I do it?
Here is my code so far
exports.logcat = function(options, callback){
var logcatCmd = "adb logcat" + options;
console.log("sending command: " + logcatCmd);
exec(logcatCmd, function(error, stdout, stderr){
if(error !== null){
console.log("encountered error = " + error);
}
callback(stdout);
});
};
In short, no; you cannot watch shell commands for new outputs, in the same way you can stream data from a changing file or socket.
If it generates constant output, it's more likely it just reads from some log file and prints
it on screen. You should figure out which file it's getting the data from, and use fs.watch with readableStream to continuously get the stream of data.
I have seen like a million different log functions , like log.i , log.v , log.d .
I just want to output a simple string on the log some times in my code to see if everything is working ok for debugging.
What is the clean way to do that?
You can use Log to track the logs in your application code.
Log API is for sending log output.
Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e() methods.
The order in terms of verbosity(wordiness), from least to most is ERROR, WARN, INFO, DEBUG, VERBOSE. Otherwise more or less they are same.
Verbose should never be compiled into an application except during development.
Debug logs are compiled in but stripped at runtime.
Error, warning and info logs are always kept.
example
String TAG = "value of i = ";
for(int i = 0; i<=10 i++)
{
Log.i(TAG, i+"");
}
this will print the 10 numbers in your log.i (info).
log.d
is for debug
log.e
is for error
and so on. here is a link to study more about log
Log.d("tag","the string youd like");
log.d is for the debug list of LogCat
When you change the letters after log. you are basically setting the severity of this log record:
I => Info
V => Verbose
D => Debug
E => Error
Take a look at this picture for all the different kinds of log records:
So use .d for simple debugging records like this:
Log.d (TAG, "the message you want to out put");
While I always set the TAG at the begging of the class like this:
static final String TAG = YouCurrentActivity.class.getSimpleName();
#donparalias great question
The Most easiest way:
public static final String LOG_TAG = "Facebook";
public void onFacebookError(FacebookError error)
{
Log.i(Facebook.LOG_TAG, "LoginListener onFacebookError " + error);
}
You can use one or all of these:
Log.d("tag","string"); :: Debug
Log.v("tag","string"); :: Verbose
Log.e("tag","string"); :: Error
Log.i("tag","string"); :: Info
there are different type of log.
I = Info
V = Verbose
D = Debug
E = Error
Example:
Log.d("tag","the string youd like");
Log.v("tag","the string youd like");
Log.e("tag","the string youd like");
Log.i("tag","the string youd like");